4 votes

Comment tester / simuler un appel à $timeout ?

Comment puis-je me moquer de l'appel au délai d'attente ?

$scope.submitRequest = function () {

    var formData = getData();

    $scope.form = JSON.parse(formData);

    $timeout(function () {
        $('#submitForm').click();            
    }, 2000);

};

Je veux voir si le timeout a été appelé avec la bonne fonction.

J'aimerais avoir un exemple de la fonction spyon qui se moque de $timeout.

spyOn(someObject,'$timeout')

5voto

Frane Poljak Points 94

Tout d'abord, la manipulation du DOM ne doit être effectuée que dans des directives. De plus, il est préférable d'utiliser angular.element(...), plutôt que $(...). Enfin, pour ce faire, vous pouvez exposer le gestionnaire de clic de votre élément au scope, l'espionner, et vérifier si ce gestionnaire a été appelé :

$timeout.flush(2000);
$timeout.verifyNoPendingTasks();
expect(scope.myClickHandler).toHaveBeenCalled();

EDIT :

puisqu'il s'agit d'un formulaire et qu'il n'y a pas de gestionnaire ng-click, vous pouvez utiliser le gestionnaire ng-submit, ou ajouter un nom à votre formulaire et le faire :

$timeout.flush(2000);
$timeout.verifyNoPendingTasks();
expect(scope.formName.$submitted).toBeTruthy();

4voto

estus Points 5252

$timeout peut être espionné ou tourné en dérision, comme le montre l'exemple suivant cette réponse :

beforeEach(module('app', ($provide) => {
  $provide.decorator('$timeout', ($delegate) => {
    var timeoutSpy = jasmine.createSpy().and.returnValue($delegate);
    // methods aren't copied automatically to spy
    return angular.extend(timeoutSpy, $delegate);
  });
}));

Il n'y a pas grand-chose à tester ici, puisque $timeout est appelé avec une fonction anonyme. Pour des raisons de testabilité, il est logique de l'exposer en tant que méthode scope/controller :

$scope.submitFormHandler = function () {
    $('#submitForm').click();            
};

...
$timeout($scope.submitFormHandler, 2000);

Puis a espionné $timeout peuvent être testés :

$timeout.and.stub(); // in case we want to test submitFormHandler separately
scope.submitRequest();
expect($timeout).toHaveBeenCalledWith(scope.submitFormHandler, 2000);

Et la logique à l'intérieur $scope.submitFormHandler peut être testé de différentes manières.

Un autre problème est que jQuery ne fonctionne pas bien avec les tests unitaires et nécessite d'être testé contre le DOM réel (c'est l'une des nombreuses raisons pour lesquelles jQuery devrait être évité dans les applications AngularJS lorsque c'est possible). Il est possible d'espionner/mocker l'API jQuery comme le montre l'exemple suivant cette réponse .

$(...) peut être espionnée :

var init = jQuery.prototype.init.bind(jQuery.prototype);
spyOn(jQuery.prototype, 'init').and.callFake(init);

Et on peut s'en moquer :

var clickSpy = jasmine.createSpy('click');
spyOn(jQuery.prototype, 'init').and.returnValue({ click: clickSpy });

Notez qu'il est prévu que la fonction simulée renvoie un objet jQuery pour l'enchaînement avec click méthode.

Quand $(...) est simulé, le test n'exige pas que l'option #submitForm à créer dans le DOM, c'est la méthode préférée pour les tests unitaires isolés.

1voto

Petr Averyanov Points 6555

Créer un simulacre pour le fournisseur $timeout :

var f = () => {} 
var myTimeoutProviderMock = () => f;

Utilisez-le :

beforeEach(angular.mock.module('myModule', ($provide) => {
  $provide.factory('$timeout', myTimeoutProviderMock);
}))

Vous pouvez maintenant tester :

spyOn(f);
expect(f).toHaveBeenCalled();

P.S. Vous feriez mieux de tester le résultat de la fonction dans le délai d'attente.

1voto

PM1 Points 31

En supposant que ce morceau de code se trouve dans le contrôleur ou qu'il est créé dans le test par $controller, $timeout peut être passé dans le paramètre de construction. Vous pouvez donc faire quelque chose comme

var timeoutStub = sinon.stub();
var myController = $controller('controllerName', timeoutStub);
$scope.submitRequest();
expect(timeoutStub).to.have.been.called;

1voto

Manoj Bhardwaj Points 550

Unité Test de $timeout avec délai de rinçage

Vous devez vider la file d'attente du service $timeout en appelant $timeout.flush()

describe('controller: myController', function(){
describe('showAlert', function(){
    beforeEach(function(){
        // Arrange
        vm.alertVisible = false;

        // Act
        vm.showAlert('test alert message');
    });

    it('should show the alert', function(){
        // Assert
        assert.isTrue(vm.alertVisible);
    });

    it('should hide the alert after 5 seconds', function(){
        // Act - flush $timeout queue to fire off deferred function
        $timeout.flush();

        // Assert
        assert.isFalse(vm.alertVisible);
    });
  })
});

Veuillez consulter ce lien http://jasonwatmore.com/post/2015/03/06/angularjs-unit-testing-code-that-uses-timeout

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X