0

We're unit testing our services and facing issue spying on methods with arguments of dependent services.

I am writing unit tests for ServiceA

ServiceA.js

angular.module("App").service("ServiceA", function($http, ServiceB) {
    this.detail = null; 
    this.method = function(id){
        var sevrB = new ServiceB();
        return sevrB.getId(1).then(function(response) {
            this.detail = response.data;
        }); 
    };
 });

ServiceB.js (is a factory)

(function () {
    var dependencies = [
      '../module'
    ];
    define(dependencies, function (module) {
        return module.factory('ServiceB', function ($http) {
            var ServiceB= function () {
                this.id = null;               
            };

        ServiceB.prototype.getId = function(Id) {                               
            return $http.get('/test/');
        }
    }
 }());

Unit test code

 describe('Testing ServiceA', function () {
 var serviceA, serviceBMock;

 beforeEach(function () {
     var _serviceBMock = function () {
         return {
             getId:function(id){
                 return 'test';
             }
         };
     };

     angular.module('ServiceAMocks', [])                   
         .value('ServiceB', _serviceBMock);
 });

beforeEach(module('ServiceAMocks'));       

beforeEach(inject(function (_ServiceA_, _ServiceB_) {
    serviceA=_ServiceA_;
    serviceBMock=_ServiceB_;
});

it('retrive Id', function () {  
   spyOn(serviceBMock,'getId').and.Return('test');
   serviceA.method(1);
});

}); 

I am spying on getId method of ServiceB from ServiceA and if i mocked ServiceB as function i am getting error below

Error: getId() method does not exist at jasmineInterface.spyOn

If I mock serviceB as object then i get error as

 TypeError: object is not a function

 var _serviceBMock = {              
     getId:function(id){
         return 'test';
     }
 }

And I am not sure of testing promise in this scenario.

Krzysztof Safjanowski
  • 7,292
  • 3
  • 35
  • 47
svp
  • 495
  • 1
  • 12
  • 26
  • Is this correct: `return ServiceB.getId(1)`? I suggest to return `ServiceB.getId(id)` – Krzysztof Safjanowski Aug 03 '14 at 23:48
  • My ServiceB is a factory and acts as object model and i have created instance of this factory using new operator in Service A.I have edited the content above. – svp Aug 04 '14 at 00:39
  • There is no need to call factory with new as long as you are not returning the function from it. – Krzysztof Safjanowski Aug 04 '14 at 00:49
  • Thank you,I have updated the exact definition of my SevriceB factory in above content and i return it as a function().Then i use new operator to convert it into an object.How to unit test for this kind of scenario?? – svp Aug 04 '14 at 01:13
  • Hi,I could fix some part this issue by using new in beforeEach block as serviceBMock=new _ServiceB_();.....but now i need to know how to test promises that is returned by serviceB.getId...... – svp Aug 04 '14 at 01:56
  • Please try to explain what is the reason to insatiate `serviceB` inside `ServiceA`? Sometimes I’ve created constructor, but they are instantiated inside **config** section and returned as resolved promise. – Krzysztof Safjanowski Aug 04 '14 at 13:02

1 Answers1

0

This version supports Jasmine 1.3

I’m injecting $q service as ServiceB wants to call method then. We can even go forward and resolve returned promise, but this is next step in testing.

Answer to previous version of question, where AngularJS injects instance of serviceB

describe('ServiceA', function () {
    var serviceA, ServiceB, $q;

    beforeEach(function () {
        module('App');
    });

    beforeEach(function () {
        module(function ($provide) {
            $provide.value('ServiceB', {
                getId: jasmine.createSpy('ServiceB.getId').andCallFake(function () {
                    return $q.all();
                })
            });
        });
    });

    beforeEach(inject(function (_ServiceA_, _ServiceB_, _$q_) {
        $q = _$q_;
        serviceA = _ServiceA_;
        ServiceB = _ServiceB_;
    }));

    describe('.method()', function () {
        it('returns ServiceB.getId() argument', function () {
            serviceA.method(1);
            expect(ServiceB.getId).toHaveBeenCalledWith(1);
        });
    });
});

jsfiddle: http://jsfiddle.net/krzysztof_safjanowski/sDh35/

Krzysztof Safjanowski
  • 7,292
  • 3
  • 35
  • 47
  • Hi this solution is good but you have missed my new operator.I have edited my post above and created serviceB as factory and i use this as object model and use new operator to create instance of that factory and call methods of serviceB.My code is throwing the TypeError: object is not a function. – svp Aug 04 '14 at 00:41
  • This is not working if ServiceB is a factory returning as function and use new operator in beforeEach block. – svp Aug 04 '14 at 04:48