3

I'm trying to test an angular service that returns a promise in mocha chai, but calling 'then' in a unit test times out. My service depends on a second service with an async $resource request. myService.getData() should internally get data from said asynchronous service and pass on the promise to the controller, which calls a 'then' function on the response. This works great in the controller, and absolutely fails in the unit test.

Service code:

// use: myService.getData.then(...) works beautifully in angular controllers
getData = function(dataId) {
    var dataPromise;

    // get students from the api, if necessary, or from the service if already retrieved
    // dataList is a private property of the service
    if(!dataList) {
        dataPromise = asyncServiceUsedByMyService.asyncMethod({...}).$promise
            .then(
                function(response){


                  dataList = response;

                  doSomeDataManipulation();

                      return dataList;
                },
                function(err) {
                    return err;
                }
            );
    }
    // use the existing studentList if available
    else {
        dataPromise = $q.when(function(){
            return dataList
        });
    }

    return dataPromise;
};

Test Code

describe('Service Test', function() {
    var expect = chai.expect,
        myService,
        asyncServiceUsedByMyService;


    beforeEach(function() {

        var data,
            asyncServiceMock;

        data =[...];

        // mock the async dependency
        asyncServiceMock = {
            asynchMethod: function () {
                var deferred = q.defer();

                deferred.resolve(data);
                return {$promise: deferred.promise};
            }
        };

        module('myApp');

        module(function($provide){
            $provide.value('asyncServiceUsedByMyService', asyncServiceMock);
        });

        inject(function(myService, $q, asyncServiceUsedByMyService) {
            myService = myService;
            q = $q;
            courseEnrollmentService = courseEnrollmentServiceMock;
        });
    });

    // passes
    it('should be available', function() {
        expect(!!myService).to.equal(true);
    });

    // 'then' function is never called, unit test times out
    it('should get all items from async dependency', function(done) {
        myService.getData().then(function(response) {
            expect(response).to.have.property('length').that.equals(5);
            done();
        });
    });

});

How do I get the 'then' function of myService to run in the unit test, so it will get the mocked data (which should be near instantaneous, since we're not making an actual api call?).

Note: I've also tried 'chai-as-promised' syntax, but the following seems to time out

it('should get all items from async dependency', function(done) {
    expect(myService.getData()).to.eventually.have.property('length').that.equals(5).and.notify(done);
});
ansorensen
  • 1,276
  • 1
  • 14
  • 29

1 Answers1

4

The promises resolution in angular is made in the digest cycle, you can trigger this cycle by calling scope.$apply(); in your test.

You can read more here.

Community
  • 1
  • 1
Federico Nafria
  • 1,397
  • 14
  • 39
  • Adding scope.$apply() forces the test to run and pass or fail, but karma does have an error that some of my tests cause a full page reload. Also (maybe related to the reload?) I can't debug my tests any more. When I try to debug my Karma tests in Chrome (default karma browser), I get NOT FOUND (404) errors and I can't find the javascript sources in the Chrome dev tools. Is there a better way to test async promises without 'then' and 'apply,' since Karma apparently doesn't handle apply well? – ansorensen Oct 20 '14 at 19:10
  • I've been using Karma, but with Jasmine and promises were never a problem, so I don't really know what is happening to your tests. – Federico Nafria Oct 21 '14 at 03:41