Angular unit-test controllers - mocking service inside controller Angular unit-test controllers - mocking service inside controller angularjs angularjs

Angular unit-test controllers - mocking service inside controller


There are two ways (or more for sure).

Imagining this kind of service (doesn't matter if it is a factory):

app.service('foo', function() {  this.fn = function() {    return "Foo";  };});

With this controller:

app.controller('MainCtrl', function($scope, foo) {  $scope.bar = foo.fn();});

One way is just creating an object with the methods you will use and spy them:

foo = {  fn: function() {}};spyOn(foo, 'fn').andReturn("Foo");

Then you pass that foo as a dep to the controller. No need to inject the service. That will work.

The other way is to mock the service and inject the mocked one:

beforeEach(module('app', function($provide) {  var foo = {    fn: function() {}  };    spyOn(foo, 'fn').andReturn('Foo');  $provide.value('foo', foo);}));

When you inject then foo it will inject this one.

See it here: http://plnkr.co/edit/WvUIrtqMDvy1nMtCYAfo?p=preview

Jasmine 2.0:

For those that struggle with making the answer work,

as of Jasmine 2.0 andReturn() became and.returnValue()

So for example in the 1st test from the plunker above:

describe('controller: MainCtrl', function() {  var ctrl, foo, $scope;  beforeEach(module('app'));    beforeEach(inject(function($rootScope, $controller) {    foo = {      fn: function() {}    };        spyOn(foo, 'fn').and.returnValue("Foo"); // <----------- HERE        $scope = $rootScope.$new();        ctrl = $controller('MainCtrl', {$scope: $scope , foo: foo });  }));    it('Should call foo fn', function() {    expect($scope.bar).toBe('Foo');  });});

(Source: Rvandersteen)