1

I'm trying to mock a service I'm using and should return a promise, the mock service is being called but I can't get the result to my test.

service function to be tested:

function getDevices() {
            console.log('getDevices');
            return servicesUtils.doGetByDefaultTimeInterval('devices')
                .then(getDevicesComplete);

            function getDevicesComplete(data) {
                console.log('getDevicesComplete');
                var devices = data.data.result;
                return devices;
            }
        }

My test is:

describe('devicesService tests', function () {
    var devicesService;
    var servicesUtils, $q, $rootScope;

    beforeEach(function () {
        servicesUtils = {};

        module('app.core', function ($provide) {
            servicesUtils = specHelper.mockServiceUtils($provide, $q, $rootScope);
        });

        inject(function (_devicesService_, _$q_, _$rootScope_) {
            devicesService = _devicesService_;
            $q = _$q_;
            $rootScope = _$rootScope_.$new();
        });
    });

    it('getting device list', function () {
        console.log('getting device list');
        devicesService.getDevices().then(function (result) {
            console.log(result);
            expect(result).toBeDefined();
        });    
    });
});

Mock file:

function mockServiceUtils($provide, $q) {
        var servicesUtils = {};
        servicesUtils.doGetByDefaultTimeInterval = jasmine.createSpy().and.callFake(function() {
            var deferred = $q.defer();
            deferred.resolve('Remote call result');
            $rootScope.$digest();
            return deferred.promise;
        });

        $provide.value('servicesUtils', servicesUtils);

        return servicesUtils;
    }
Dor Cohen
  • 16,769
  • 23
  • 93
  • 161
  • you need to use $rootScope.$digest in order for the promise to fire – Dayan Moreno Leon Oct 25 '15 at 08:48
  • @DayanMorenoLeon I added the digest but although the scope defined the digest method is undefined, I edited my question with the changes – Dor Cohen Oct 25 '15 at 09:18
  • well you mock does not define a scope, your test ddi. so try to execute the digest after getDevices is called – Dayan Moreno Leon Oct 25 '15 at 09:32
  • @DayanMorenoLeon I can't get it to work, digest is now defined but I still can't get the promise result in my test – Dor Cohen Oct 25 '15 at 09:58
  • mockServiceUtils takes $provide and $q as argument, but also uses $rootScope. And the function is called with $q and $rootScope, but they aren't initialized at that point. And finally, we don't have any idea of what devicesService.getDevices() does. Why don't you just spy on servicesUtils rather than mocking it? Why don't you use $q.when()? – JB Nizet Oct 25 '15 at 10:29
  • @JBNizet I need serviceUtils to return an object, I created a spy for it and it suppose to return me a promise. I followed the example from this answer: http://stackoverflow.com/questions/23705051/how-do-i-mock-a-service-that-returns-promise-in-angularjs-jasmine-unit-test – Dor Cohen Oct 25 '15 at 10:32
  • I'll add an answer explaining a much simpler (and correct) way of doing. – JB Nizet Oct 25 '15 at 10:35
  • @JBNizet many thanks, I also added the getDevices function – Dor Cohen Oct 25 '15 at 10:36
  • That function doesn't make much sense. A function passed to then() is called with a single argument, not 4. And that function returns an undefined variable: devices. – JB Nizet Oct 25 '15 at 10:44
  • @JBNizet my mistake, I fixed the function, now I will try your solution, many thanks~ – Dor Cohen Oct 25 '15 at 10:46

1 Answers1

1

Your code is way too complex.

Let's assume that you want to test a service devicesService that uses another service servicesUtils, having a method that returns a promise. Let's assume devicesService's responsibility is to call servicesUtils and transform its result.

Here's how I would do it:

describe('devicesService', function() {

  var devicesService, servicesUtils;

  beforeEach(module('app.core'));
  beforeEach(inject(function(_devicesService_, _servicesUtils_) {
    devicesService = _devicesService_;
    servicesUtils = _servicesUtils_;
  }));

  it('should get devices', inject(function($q, $rootScope) {
    spyOn(servicesUtils, 'doGetByDefaultTimeInterval').and.returnValue($q.when('Remote call result'));

    var actualResult;
    devicesService.getDevices().then(function(result) {
      actualResult = result;
    });
    $rootScope.$apply();

    expect(actualResult).toEqual('The transformed Remote call result');
  }));
});
JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • Thanks, it works! I wonder what I should do in case I want the servicesUtils spy will be on a different file that will be accessible from several test files. that's what I tried to do but it didn't work for me, any thoughts? – Dor Cohen Oct 25 '15 at 11:59
  • I don't see the point. Every test should have its own scenario, and should thus spy the service differently. – JB Nizet Oct 25 '15 at 12:09