Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular unit-test controllers - mocking service inside controller

Tags:

I have the following situation:

controller.js

controller('PublishersCtrl',['$scope','APIService','$timeout', function($scope,APIService,$timeout) {

    APIService.get_publisher_list().then(function(data){

            });
 }));

controllerSpec.js

'use strict';

describe('controllers', function(){
    var scope, ctrl, timeout;
    beforeEach(module('controllers'));
    beforeEach(inject(function($rootScope, $controller) {
        scope = $rootScope.$new(); // this is what you missed out
        timeout = {};
        controller = $controller('PublishersCtrl', {
            $scope: scope,
            APIService: APIService,
            $timeout: timeout
        });
    }));

    it('should have scope variable equals number', function() {
      expect(scope.number).toBe(3);
    });
});

Error:

 TypeError: Object #<Object> has no method 'get_publisher_list'

I also tried something like this, and it didn't work:

describe('controllers', function(){
    var scope, ctrl, timeout,APIService;
    beforeEach(module('controllers'));

    beforeEach(module(function($provide) {
    var service = { 
        get_publisher_list: function () {
           return true;
        }
    };

    $provide.value('APIService', service);
    }));

    beforeEach(inject(function($rootScope, $controller) {
        scope = $rootScope.$new(); 
        timeout = {};
        controller = $controller('PublishersCtrl', {
            $scope: scope,
            APIService: APIService,
            $timeout: timeout
        }
        );
    }));

    it('should have scope variable equals number', function() {
      spyOn(service, 'APIService');
      scope.get_publisher_list();
      expect(scope.number).toBe(3);
    });
});

How can i solve this? any suggestions?

like image 844
Liad Livnat Avatar asked Dec 29 '13 17:12

Liad Livnat


People also ask

How do you mock a component in Angular unit testing?

A mock component in Angular tests can be created by MockComponent function. The mock component respects the interface of its original component, but all its methods are dummies. To create a mock component, simply pass its class into MockComponent function.

What controller do you use in Angular to mock remote data for testing?

The HttpClientTestingModule allows you to easily mock HTTP requests by providing you with the HttpTestingController service.


1 Answers

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)

like image 121
Jesus Rodriguez Avatar answered Oct 12 '22 15:10

Jesus Rodriguez