Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS override (mock) services for unit test

Tags:

angularjs

The scenario is very simple, I am trying to test a service A, which depends on service B, so I decided to mock service B by overriding it before I inject service A. However, I am also testing service B which is no longer valid since it is overridden. Any idea on how to avoid this?

Application code.

angular.module('MyApp.services')
  .factory('serviceB', function(){
    return {
      greeting: function(){ return 'hello'; }
    }
  })
  .factory('serviceA', ['serviceB', function(serviceB){
    return {
      greeting: function(){
        return 'I say: ' + serviceB.greeting();
      }
  });

Unit Test:

describe('My app services', function(){

  beforeEach(module('MyApp.services'));

  describe('service A', function(){
      // mock service B
    angular.module('MyApp.services').factory('serviceB', function(){
      return { greeting: function(){ return 'mocked B'; } }
    });

    var serviceA;

    inject(function(_serviceA_){
      serviceA = _serviceA_;
    });

    it('should work', function(){
      var words = serviceA.greeting();
      expect(words).toBe('I say: mocked B');          
    });
  });

  describe('service B'. function(){
    var serviceB;

    inject(function(_serviceB_){
      serviceB = _serviceB_;
    });

    it('should work', function(){
      var words = serviceB.greeting();
      expect(words).toBe('hello');
    });
  });
});
like image 226
Rn2dy Avatar asked Dec 20 '22 21:12

Rn2dy


1 Answers

Your service injection should probably be either in a beforeEach or as part of the tests, otherwise I think they will happen before the tests begin or not at all, e.g.

beforeEach(inject(function(_serviceA_){
  serviceA = _serviceA_;
}));

or

it('should work', inject(function(serviceA){
    var words = serviceA.greeting();
    expect(words).toBe('I say: mocked B');          
}));

To solve the problem you are actually asking about, I suggest you do one of two things:

  1. Override the service in the injector rather than the module

    beforeEach(module(function($provide){
      $provide.value('serviceB', {
        greeting: function(){ return 'mocked B'; }
      });
    }));
    

    See plunker.

  2. Use a separate module that can be added when needed to override serviceB

    Somewhere (that only needs to be called once), put the definition of your new module containing your mock version of serviceB

    angular.module('MyApp.services.mock', [])
      .factory('serviceB', function(){
        return { greeting: function(){ return 'mocked B'; } }
      });
    

    then where you currently alter the service in your module under test, use the following to load your module with the mock on top.

    beforeEach(module('MyApp.services.mock'));
    

    See plunker

like image 193
Andyrooger Avatar answered Dec 30 '22 18:12

Andyrooger