web-dev-qa-db-fra.com

Le service de test unitaire qui revient promet Angularjs Jasmine

EDITÉ par poste Michal Charemza.

J'ai un service qui représente le dialogue modal angularui:

app.factory("dialogFactory", function($modal, $window, $q) {

    function confirmDeleteDialog() {

    var modalInstance = $modal.open({
        templateUrl: "../application/factories/confirmDeleteDialog.htm",
        controller: function($scope, $modalInstance) {

            $scope.ok = function() {
                $modalInstance.close("true");
            };

            $scope.cancel = function() {
                $modalInstance.dismiss("false");
            };
        }
    });


    return modalInstance.result.then(function(response) {
        return 'My other success result';
    }, function(response) {
        return $q.reject('My other failure reason');
    });

};

    return {
        confirmDeleteDialog: confirmDeleteDialog
    };

});

Lors de l'appel de la méthode de suppression si l'utilisateur a cliqué sur Ok dans la boîte de dialogue requestNotificationChannel.deleteMessage(id) est exécuté.

$scope.deleteMessage = function(id) {
        var result = dialogFactory.confirmDeleteDialog();

        result.then(function(response) {
            requestNotificationChannel.deleteMessage(id);
        });
    };

Le problème est que je ne suis pas en mesure de le tester à l'unité.

Ceci est mon test. J'ai correctement injecté le service q mais je ne sais pas quoi devrais-je retourner de "confirmDeleteDialog" Spy ...

describe("has a delete method that should call delete message notification", function() {
            var deferred = $q.defer();
            spyOn(dialogFactory, "confirmDeleteDialog").and.returnValue(deferred.promise);

            spyOn(requestNotificationChannel, "deleteMessage");

            $scope.deleteMessage(5);
            deferred.resolve();

            it("delete message notification is called", function() {
                expect(requestNotificationChannel.deleteMessage).toHaveBeenCalled();
            });
        });

Mais je reçois expected spy deleteMessage to have been called. Ce qui signifie que la partie result.then ... n'est pas exécutée. Qu'est-ce que je rate ?

26
Mdb

Pour se moquer d'une fonction qui renvoie une promesse, elle devra également renvoyer une promesse, qui doit ensuite être résolue comme une étape distincte.

Dans votre cas, la deferred.resolve() que vous passez à l'espion doit être remplacée par deferred.promise, Et le deferred.resolve () effectué séparément.

beforeEach(function() {
  var deferred = $q.defer();
  spyOn(dialogFactory, "confirmDeleteDialog").and.returnValue(deferred.promise);
  spyOn(requestNotificationChannel, "deleteMessage");
  $scope.deleteMessage(5);
  deferred.resolve();
  $rootScope.$digest();
});

it("delete message notification is called", function() {
  expect(requestNotificationChannel.deleteMessage).toHaveBeenCalled();
});

Je soupçonne que vous devez également appeler $rootScope.$digest(), car la mise en œuvre de la promesse d'Angular est liée à la boucle de résumé.

Aussi, légèrement sans rapport avec votre question, mais je ne pense pas que vous ayez besoin de créer votre propre objet différé dans confirmDeleteDialog. Le motif (anti) que vous utilisez a été étiqueté "La promesse oubliée", comme dans http://taoofcode.net/promise-anti-patterns/

Quand est plus simple, utilise moins de code, et je pense que cela permet une meilleure gestion des erreurs, vous pouvez simplement retourner la promesse que le service $modal Crée:

var modalInstance = $modal.open({...});
return modalInstance.result;

Si vous souhaitez modifier ce que la fonction appelante voit, en termes de valeurs résolues/rejetées, vous pouvez créer une promesse chaînée en renvoyant le résultat de then:

var modalInstance = $modal.open({...});
return modalInstance.result.then(function(successResult) {
  return 'My other success result';
}, function(failureReason) {
  return $q.reject('My other failure reason');
});

Vous voudrez généralement le faire si vous ne voulez pas exposer le fonctionnement interne d'une fonction à son appelant. Ceci est analogue au concept de relancer une exception dans la programmation synchrone.

40
Michal Charemza