7

I have an Angular App and I want to create unit test cases for it using jasmine.
In my AngularJS app, I have a service as :

var canceler;
var myServices = angular.module('myServices', ['ngResource'])

myServices.factory('getData', ['$http', '$q', function($http, $q){
    var canceler;
    return {
        setData: function(url, callback, error) {
                    canceler = $q.defer();
                    $http.post(url, {}, {timeout:canceler.promise}).success(callback).error(error);
        },
        abort: function(){ canceler.resolve();}
    }
}]);

This service is being used by controller.

Now how can I provide a mock for this "getData" service to the injector I am using in controllerSpecs.js (for the unit testing using jasmine).

For the reference, the code of controllerSpecs.js is defined in Getting error while using Jasmine with AngularJS.

2 Answers 2

11

You can use AngularJS's $provide service. This page demonstrates how to mock the functionality of a service using Jasmine's spies. Basically in your Jasmine test you can include:

var getDataMock;

beforeEach(function() {
  getDataMock = {
    setData: jasmine.createSpy(), 
    abort: jasmine.createSpy()
  };

  module(function($provide) {
    $provide.value('getData', getDataMock);
  });
});

This tells AngularJS that instead of using the real getData service, the mock will be used in its place. In this example, using a Jasmine spy for the mock lets you easily create expectations about how and when the getData service is called by the controller. You can alternatively set getDataMock to any other object, though it makes sense to support the same API as the real getData service.

For bonus points, you can specify the getDataMock object in a separate file, so that the mock may be easily used in any number of unit tests where the thing being tested uses getData. This however requires some configuration with whatever Jasmine runner you are using.

This is all assuming you wish to unit test the controller. If you want to unit test the getData service itself, AngularJS provides a nice mocking utility specifically for http requests.

Sign up to request clarification or add additional context in comments.

6 Comments

I used the getDataMock as you have written and getting following error - TypeError: Cannot read property 'running' of undefined at isSpecRunning in angular-mock.js
I used the getDataMock as you have written and getting following error - TypeError: Cannot read property 'running' of undefined at isSpecRunning in angular-mock.js
@r.bhardwaj - can you post your entire example in a JSFiddle?
I'm really needing to understand how to achieve your "bonus point". How can I specify a mock service in '$provide'?
links in answer are dead
|
2

Try something like this

//creation mock service
var mockGetData = {
  data: [],
  setData: function(url, callback, error){
    //emulate real work, any logic can be here
    data.push('called');
    callback(data);
  }
}

and use of the mock service

it('test description', inject(function ($rootScope, $controller){
  var scope = $rootScope.$new();
  var ctrl = $controller('TodoController', { $scope: scope, getData: mockGetData });
  //some test here....
}))

4 Comments

I am unable to understand that when we are putting the blank data and a function in the name of mockgetdata that does nothing, what will happen when I will call same function from jasmin to test it's functionality ?
@r.bhardwaj, you can emulate work of real GetData in the setData function, data property is not necessary, you just can save there some data during the test. I have updated the answer, but I don't know logic of your service so you can use any logic there.
got the point but one more thing to ask - if instead of creating the mock i get the 'getData' service using $injector.get( 'getData' ), what's harm in that ?
no harm. But unit testing means that you test each block independent from each other. So if your test fails and you use mock service, you can be sure that problem is not in the service. Otherwise you need more time to find the cause of the error.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.