Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to inject a mock of a service from an external file, in another service

I am testing a service 1 depending on another service B. The correct way to usually do it is to use the $provide object before injecting A. However, I have two constraints :

  • The mock of the service B is stored in another file, since I reuse it in multiple tests (as shown later, I simply register a new Angular Service)
  • I need to initialise this service B at some points in my test (setting up promises)

The problem is, so far (I was testing controllers), I to be able to access the service B from my test, I need to inject it. But to mock it in the $provide I need to already have access to it in my test, which poses a problem, since the $provide needs to be used before any inject(). Here is my test:

describe('viewsListService', function() {
    var viewList,
        queryDeferred,
        mockViews,
        $scope;

    beforeEach(module('BoardMocks'));
    beforeEach(module('Board'));

    beforeEach(inject(function(mockViewsService) {
        var query = {};
        mockViewsService.init({}, query);
        queryDeferred = query.deferred;

        mockViews = mockViewsService.mock;
    }));

    beforeEach(function() {
        angular.mock.module('Board', function ($provide) {
            $provide.value('Views', mockViews);
        });
    });

    beforeEach(inject(function(viewsListService, $rootScope) {
        $scope = $rootScope.$new();
        viewList = viewsListService;

        // Initialisation of the viewsListService
        viewList.init();
        queryDeferred.resolve([1]);
        $scope.$digest();
    }));

    describe('getAllViews', function() {
        var allViews;

        beforeEach(function() {
            allViews = viewList.getAllViews();
        });

        it('should return all the views', function() {
            expect(allViews.length).toBe(1);
        });
    });
});

This gives me an Error: Injector already created, can not register a module!, pointing to the angular.mock.module call.

I moved my mock service to another module, thinking that perhaps it would solve the problem, wondering if the injector was specific to a module, but it doesn't seem to be (moving the beforeEach(module('Board')); after the first inject() doesn't fix the problem.

Do you have any idea to make it work, while keeping the mock in external file (I am fine with not registering it as an Angular Service, but just to a normal object, if it can solve this).

like image 764
Morphilos Avatar asked Oct 20 '22 16:10

Morphilos


1 Answers

After a lot of looking around, I found an answer by Valentyn Shybanov that explain how to do this. It is quite simple in fact. I'll leave it here for other losts souls.

Actually in AngularJS Dependency Injection uses the 'last wins' rule. So you can define your service in your test just after including your module and dependencies, and then when service A that you're testing will request service B using DI, AngularJS will give mocked version of service B.

So if you want to mock the "Views" service. You create a "BoardMocks" module that contains a "Views" service. If "BoardMocks" is included after "Board", the mocked "Views" service will be used during the tests.

See the original answer here : Injecting dependent services when unit testing AngularJS services

like image 75
user3220633 Avatar answered Oct 23 '22 07:10

user3220633