web-dev-qa-db-fra.com

Se moquer d'un service pour tester un contrôleur

J'ai un ParseService, que je voudrais simuler afin de tester tous les contrôleurs qui l'utilisent, j'ai lu sur les espions de jasmin mais ce n'est toujours pas clair pour moi. Quelqu'un pourrait-il me donner un exemple de la façon de se moquer d'un service personnalisé et de l'utiliser dans le test du contrôleur?

En ce moment, j'ai un contrôleur qui utilise un service pour insérer un livre:

BookCrossingApp.controller('AddBookCtrl', function ($scope, DataService, $location) {

    $scope.registerNewBook = function (book) {
        DataService.registerBook(book, function (isResult, result) {

            $scope.$apply(function () {
                $scope.registerResult = isResult ? "Success" : result;
            });
            if (isResult) {
                //$scope.registerResult = "Success";
                $location.path('/main');
            }
            else {
                $scope.registerResult = "Fail!";
                //$location.path('/');
            }

        });
    };
});

Le service est comme ça:

angular.module('DataServices', [])

    /**
     * Parse Service
     * Use Parse.com as a back-end for the application.
     */
    .factory('ParseService', function () {
        var ParseService = {
            name: "Parse",

            registerBook: function registerBook(bookk, callback) {

                var book = new Book();

                book.set("title", bookk.title);
                book.set("description", bookk.Description);
                book.set("registrationId", bookk.RegistrationId);
                var newAcl = new Parse.ACL(Parse.User.current());
                newAcl.setPublicReadAccess(true);
                book.setACL(newAcl);

                book.save(null, {
                    success: function (book) {
                        // The object was saved successfully.
                        callback(true, null);
                    },
                    error: function (book, error) {
                        // The save failed.
                        // error is a Parse.Error with an error code and description.
                        callback(false, error);
                    }
                });
            }
        };

        return ParseService;
    });

Et jusqu'à présent, mon test ressemble à ceci:

describe('Controller: AddBookCtrl', function() {

    //  // load the controller's module
    beforeEach(module('BookCrossingApp'));


    var AddBookCtrl, scope, book;

    // Initialize the controller and a mock scope
    beforeEach(inject(function($controller, $rootScope) {
        scope = $rootScope;
        book = {title: "fooTitle13"};
        AddBookCtrl = $controller('AddBookCtrl', {
            $scope: scope
        });
    }));

    it('should call Parse Service method', function () {

        //We need to get the injector from angular
        var $injector = angular.injector([ 'DataServices' ]);
        //We get the service from the injector that we have called
        var mockService = $injector.get( 'ParseService' );
        mockService.registerBook = jasmine.createSpy("registerBook");
        scope.registerNewBook(book);
        //With this call we SPY the method registerBook of our mockservice
        //we have to make sure that the register book have been called after the call of our Controller
        expect(mockService.registerBook).toHaveBeenCalled();
    });
    it('Dummy test', function () {
        expect(true).toBe(true);
    });
});

Pour l'instant, le test échoue:

   Expected spy registerBook to have been called.
   Error: Expected spy registerBook to have been called.

Qu'est ce que je fais mal?

42

Ce que je faisais de mal, ce n'est pas d'injecter le service simulé dans le contrôleur au préalable.

describe('Controller: AddBookCtrl', function() {

    var scope;
    var ParseServiceMock;
    var AddBookCtrl;

    // load the controller's module
    beforeEach(module('BookCrossingApp'));

    // define the mock Parse service
    beforeEach(function() {
        ParseServiceMock = {
            registerBook: function(book) {},
            getBookRegistrationId: function() {}
       };
   });

   // inject the required services and instantiate the controller
   beforeEach(inject(function($rootScope, $controller) {
       scope = $rootScope.$new();
       AddBookCtrl = $controller('AddBookCtrl', {
           $scope: scope,
           DataService: ParseServiceMock
       });
   }));

   it('should call registerBook Parse Service method', function () {
       var book = {title: "fooTitle"}

       spyOn(ParseServiceMock, 'registerBook').andCallThrough();
       //spyOn(ParseServiceMock, 'getBookRegistrationId').andCallThrough();
       scope.registerNewBook(book);

       expect(ParseServiceMock.registerBook).toHaveBeenCalled();
       //expect(ParseServiceMock.getBookRegistrationId).toHaveBeenCalled();
    });
});
60

Vous pouvez injecter votre service, puis utiliser spyOn.and.returnValue () comme ceci:

beforeEach(angular.mock.module('yourModule'));

beforeEach(angular.mock.inject(function($rootScope, $controller, ParseService) {
    mock = {
        $scope: $rootScope.$new(),
        ParseService: ParseService
    };
    $controller('AddBookCtrl', mock);
}));

it('should call Parse Service method', function () {
    spyOn(mock.ParseService, "registerBook").and.returnValue({id: 3});

    mock.$scope.registerNewBook();

    expect(mock.ParseService.registerBook).toHaveBeenCalled();
});
11
dduft

À la suite de Javito réponse 4 ans après coup. Jasmine a changé sa syntaxe en 2.0 pour appeler des méthodes réelles sur les espions.

Changement:

spyOn(ParseServiceMock, 'registerBook').andCallThrough();

à:

spyOn(ParseServiceMock, 'registerBook').and.callThrough();

Source

2
Steve