Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing an asynchronous service in angularjs

I am trying to unit test a service which has asynchronous methods but am having no luck.

I have tried to implement with promises by using the $q support in angularjs.

Any help would be appreciated.

http://jsfiddle.net/9pBze/37/

angular.module('myapp', ['myservice']);

angular.module('myservice', []).factory('myservice', function($q) {
  var ls = {};

  ls.DoIt = function() {
    var deferred = $q.defer();

    setTimeout(function(){
        deferred.resolve(5);
    },3000);

    return deferred.promise;
  }

  return ls;

});

describe('services', function () {

    beforeEach(module('myservice'));

    it("should equal 2",  inject(function(myservice) {
        myservice.DoIt().then(function(returned) {
            expect(returned).toEqual(2);
        });        
    }));
});
like image 929
Hidden Developer Avatar asked Mar 18 '13 12:03

Hidden Developer


People also ask

Can unit tests be async?

Unlike async void unit tests that are quite complicated, you can have async Task unit tests, i.e., unit tests that return a Task instance. Almost all the unit test frameworks (MSTest, NUnit, etc.) provide support for such unit tests.

What is async in Angular testing?

We wrap our test spec function in another function called async . 2. We place the tests we need to run after the isAuthenticated promise resolves inside this function. This async function executes the code inside its body in a special async test zone. This intercepts and keeps track of all promises created in its body.

Is AngularJS code unit testable?

AngularJS is written with testability in mind, but it still requires that you do the right thing. We tried to make the right thing easy, but if you ignore these guidelines you may end up with an untestable application.

What is unit testing in Angular?

Angular Unit testing is the process of testing small and isolated pieces of code in your Angular application. This provides an added advantage to the users in the sense that they can add any new features without breaking any other part of their application.


2 Answers

First of all, the setTimeout is particularly tricky to test since it hard to mock. Fortunately AngularJS has a wrapper around it ($timeout) that plays the same role but can be easily mocked:

  ls.DoIt = function() {
    var deferred = $q.defer();

    $timeout(function(){
        deferred.resolve(5);
    },3000);

    return deferred.promise;
  }

The mock provided for $timeout allows us to easily simulate elapsed time (with $timeout.flush()) which means our tests can run fast, without really waiting for the async event to complete (please note that the production code is still using async API!).

The changed tests would look like:

it("should equal 5",  inject(function(myservice, $timeout) {

    var valueToVerify;
    myservice.DoIt().then(function(returned) {
      valueToVerify = returned;  
    });  
    $timeout.flush();        
    expect(valueToVerify).toEqual(5);
}));

And finally the working jsFiddle: http://jsfiddle.net/v9L9G/1/

like image 100
pkozlowski.opensource Avatar answered Sep 21 '22 13:09

pkozlowski.opensource


It's not related to Angular itself, but to Jasmine async tests.

If you need a setTimeout use Angular $timeout. And if you wish to have a fine control over setTimeout/$timeout executions, use mocked Clock.

like image 41
Caio Cunha Avatar answered Sep 25 '22 13:09

Caio Cunha