67 votes

Test basé sur la promesse en code angulaire

Je vais avoir des moments difficiles en train de tester, promesse code Angulaire.

J'ai le code suivant dans mon controller:

    $scope.markAsDone = function(taskId) {
        tasksService.removeAndGetNext(taskId).then(function(nextTask) {
            goTo(nextTask);
        })
    };

    function goTo(nextTask) {
        $location.path(...);
    }

J'aimerais unité de tester les cas suivants:

  • lors de l' markAsDone est appelée, elle doit appeler tasksService.removeAndGetNext
  • lors de l' tasksService.removeAndGetNext est fait, il devrait changer d'emplacement (invoke goTo)

Il me semble qu'il n'y a pas de moyen facile de tester ces deux cas séparément.

Ce que j'ai fait pour tester le premier était:

var noopPromise= {then: function() {}}
spyOn(tasksService, 'removeAndGetNext').andReturn(noopPromise);

Maintenant pour tester deuxième cas, j'ai besoin de créer un autre faux promesse qui serait toujours resolved. C'est assez fastidieux et c'est beaucoup de code réutilisable.

Est-il un autre moyen de tester de telles choses? Ou est-ce que mon design odeur?

107voto

CaioToOn Points 9069

Vous aurez toujours besoin de se moquer des services et de retour d'une promesse, mais vous devez utiliser de vraies promesses au lieu de cela, de sorte que vous n'avez pas besoin de mettre en œuvre ses fonctionnalités. Utiliser beforeEach de créer déjà en accomplissement de la promesse et de s'en moquer si vous avez besoin d'elle pour TOUJOURS être résolu.

var $rootScope;

beforeEach(inject(function(_$rootScope_, $q) {
  $rootScope = _$rootScope_;

  var deferred = $q.defer();
  deferred.resolve('somevalue'); //  always resolved, you can do it from your spec

  spyOn(tasksService, 'removeAndGetNext').andReturn(deferred.promise); 
}));

Si vous préférez la préférez à résoudre dans chaque it bloc par une autre valeur, alors que vous venez d'exposer la reportés à une variable locale et de le résoudre dans les spécifications.

Bien sûr, vous devez garder vos tests comme ils sont, mais ici, c'est vraiment simple spec pour vous montrer comment cela pourrait fonctionner.

it ('should test receive the fulfilled promise', function() {
  var result;

  tasksService.removeAndGetNext().then(function(returnFromPromise) {
    result = returnFromPromise;
  });

  $rootScope.$apply(); // promises are resolved/dispatched only on next $digest cycle
  expect(result).toBe('somevalue');
});

4voto

Victor Alvarez Points 108

Une autre approche serait la suivante, tout droit sorti d’un contrôleur que j’étais en train de tester:

 var create_mock_promise_resolves = function (data) {
    return { then: function (resolve) { return resolve(data); };
};

var create_mock_promise_rejects = function (data) {
    return { then: function (resolve, reject) { if (typeof reject === 'function') { return resolve(data); } };
};

var create_noop_promise = function (data) {
    return { then: function () { return this; } };
};
 

0voto

Michael Points 622

Et comme autre option, vous pouvez éviter de devoir appeler $digest en utilisant la bibliothèque Q ( https://github.com/kriskowal/q ) en remplacement immédiat de $q par exemple:

 beforeEach(function () {
    module('Module', function ($provide) {
        $provide.value('$q', Q); 
    });
});
 

De cette façon, les promesses peuvent être résolues / rejetées en dehors du cycle $ digest.

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