Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mock a service in order to test a controller

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?

like image 262
Javier Hertfelder Avatar asked Apr 06 '13 17:04

Javier Hertfelder


People also ask

What are mock services in testing?

What is mock testing? Mocking means creating a fake version of an external or internal service that can stand in for the real one, helping your tests run more quickly and more reliably. When your implementation interacts with an object's properties, rather than its function or behavior, a mock can be used.

Why do we mock a service?

Mock Services help you handle situations where a piece of your application might not be available for testing when you need it. When combined with BlazeMeter performance testing functionality, Mock Services can make performance testing easier and more powerful.

What is mock data in testing?

Mock is a method/object that simulates the behavior of a real method/object in controlled ways. Mock objects are used in unit testing. Often a method under a test calls other external services or methods within it. These are called dependencies. Once mocked, the dependencies behave the way we defined them.


2 Answers

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();     }); }); 
like image 143
Javier Hertfelder Avatar answered Sep 24 '22 21:09

Javier Hertfelder


You can inject your service and then use spyOn.and.returnValue() like this:

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(); }); 
like image 42
dduft Avatar answered Sep 26 '22 21:09

dduft