Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angularjs - how to correctly replace service dependency with a mock

I'm using yeoman generator created app, and doing my tests in karma.

I have reusable mock objects for every of my service. How do i correctly replace specific service dependcy with a mock, so i could then use jasmine to spy upon methods

So far i have done like this:

My service:

angular.module('ql')
  .service('loginService', ['$http','API','authService', function ($http, API, authService) {
    return {
      //service implementation
    }]);

Mock of authService:

'use strict';
//lets mock http auth  service, so it would be spied upon.
ql.mock.$authServiceMockProvider = function() {
  this.$get = function() {
    var $service = {
      loginConfirmed: function() { }
    };
    return $service;
  };
};

//and register it.
angular.module('qlMock').provider({
  $authServiceMock: ql.mock.$authServiceMockProvider
});

And my test:

'use strict';

describe('When i call login method()', function () {

  // load the service's module
  beforeEach(module('ql'));
  beforeEach(angular.mock.module('qlMock'));

  // instantiate service
  var loginService,
  authService,
  $httpBackend;
  beforeEach(function() {
    // replace auth service with a mock.
    // this seems kind of dirty... is there a bettery way? 
    module(function($provide, $injector){
      authService = $injector.get('$authServiceMockProvider').$get();
      $provide.value('authService', authService);
    });

    //actually get the loginService
    /*jshint camelcase: false */
    inject(function(_loginService_, _$httpBackend_) {
      loginService = _loginService_;
      $httpBackend =_$httpBackend_;
    });

    //http auth module method, that should be call only on success scenarios
    spyOn(authService, 'loginConfirmed').andCallThrough();
  });
  it('it should do something', function () {
  //actual test logic
  });
});

What i do not like is the line:

authService = $injector.get('$authServiceMockProvider').$get();

I would like to simply somehow get the authServiceMock (without getting provider, and calling et method) and then inject it into loginService.

I know i could call my $authServiceMock simply authService, and provide it as a mock, so that it would always override my default implementation, but i do not want to do this.

like image 972
gerasalus Avatar asked Nov 13 '13 12:11

gerasalus


1 Answers

I know this is late but maybe it will help someone who happen upon this post.

Mocking a service in Jasmine is quite simple using Angular's $provide service. The trick is to use $provide to swap out a service implementation before injecting the service.

For example let's say we are testing a service that makes use of the $location service to get information about the current URL.

// load the service's module under test
beforeEach(module('myExampleModule'));

// mock out $location with a fake one
beforeEach(module(function ($provide) {

   //create mock impl
     var mockLocation = {
         path: function(){
                 return '/somewhere'
                 }
              }

$provide.value('$location', mockLocation); // use $provide to swap the real $location with our mock
}));

var $location;
// inject dependencies ($location will be our mocked $location)
beforeEach(inject(function (_$location_) {
 $location = _$location_;
}));

it('should return mock url', function(){
      var path = $location.path();
      expect(path).toBe('/somewhere'); //Assert that $location.path() returns '/somewhere'
});
like image 57
Nick Avatar answered Oct 26 '22 13:10

Nick