Mock a service in order to test a controller

angularjs, jasmine

Solution

What I was doing wrong is not injecting the Mocked Service into the controller in the beforeEach:

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();
    });
});

Problem

I have a ParseService, that I would like to mock in order test all the controllers that are using it, I have been reading about jasmine spies but it is still unclear for me. Could anybody give me an example of how to mock a custom service and use it in the Controller test? Right now I have a Controller that uses a Service to insert a book: ``` 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('/'); } }); }; }); ``` The service is like this: ``` 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; }); ``` And my test so far look like this: ``` 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); }); }); ``` Right now the test is failing: ``` Expected spy registerBook to have been called. Error: Expected spy registerBook to have been called. ``` What I am doing wrong?

Original source