Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Injecting dependent services when unit testing AngularJS services

I'm testing service A, but service A depends on service B (i.e. service B is injected into service A).

I've seen this question but my case is a bit different because in my opinion it makes more sense to mock service B instead of injecting an actual instance of service B. I'd mock it with a jasmine spy.

Here's a sample test:

describe("Sample Test Suite", function() {    beforeEach(function() {      module('moduleThatContainsServiceA');      inject([       'serviceA', function(service) {         this.service = service;       }     ]);    });    it('can create an instance of the service', function() {     expect(this.service).toBeDefined();   }); }); 

The error I get is:

Error: Unknown provider: serviceBProvider

How could I do something like this?

like image 890
Roy Truelove Avatar asked Jan 09 '13 15:01

Roy Truelove


People also ask

Is dependency injection required for unit testing?

Unit testing is challenging when you add frameworks around your tests subjects. Dependency injection is one of those patterns supported by frameworks, that we usually need in any project.

Does AngularJS support dependency injection?

Dependency Injection is pervasive throughout AngularJS. You can use it when defining components or when providing run and config blocks for a module.

How does dependency injection work in AngularJS?

Dependency Injection is a software design in which components are given their dependencies instead of hard coding them within the component. It relieves a component from locating the dependency and makes dependencies configurable. It also helps in making components reusable, maintainable and testable.

How do you inject service in unit testing?

The TestBed provides methods for creating components and services in unit tests. The TestBed methods are inject() , configureTestingModule() etc. To inject a service, we use TestBed. inject() method.


2 Answers

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.

This is often is done by defining new module like MyAppMocks, putting mocked services/values there and then just adding this module as dependency.

Kind of (schematically):

beforeEach(function() {   angular.module('MyAppMocks',[]).service('B', ...));   angular.module('Test',['MyApp','MyAppMocks']);   ... 
like image 110
Valentyn Shybanov Avatar answered Oct 11 '22 03:10

Valentyn Shybanov


I was doing this in CoffeeScript and found an extra gotcha. (Also, I found the code on this page to be confusingly terse.) Here's a complete working example:

describe 'serviceA', ->    mockServiceB = {}     beforeEach module 'myApp' # (or just 'myApp.services')     beforeEach ->       angular.mock.module ($provide) ->          $provide.value 'serviceB', mockServiceB          null     serviceA = null    beforeEach inject ($injector) ->       serviceA = $injector.get 'serviceA'     it 'should work', ->       expect( true ).toBe( true )       #serviceA.doStuff() 

Without explicitly returning null after $provide.value, I kept getting Error: Argument 'fn' is not a function, got Object. I found the answer in this Google Groups thread.

like image 34
jab Avatar answered Oct 11 '22 03:10

jab