Testing async service method containing nested async method call with jasmine in AngularJS

I'm trying to test what the following method returns in it's promise (functionToTest and asyncGet are both methods defined in an angularJS service):

var functionToTest = function (param) {
  var deferred = $q.defer();

  asyncGet(param).then(function (result) {
    //business login involving result

    if (something) 
      return deferred.resolve(true);
    else
      return deferred.resolve(false);
  });

  return deferred.promise;
}

The unit test looks like this:

it ('should return true', function (done) {
  var asyncGetResult = {};
  spyOn(asyncGet).and.returnValue($q.resolve(asyncGetResult));  

  var param = {};
  functionToTest(param).then(function (result) {
     expect(result).toBe(true);
     done();
  }); 

  $scope.$apply();
});

When running the test I am getting a timeout error:

Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.

I tried putting a console.log() right after the expect() but it does not print anything, so it seems like the callback for the functionToTest(param).then() is never executed.

Any help would be appreciated.

Upvotes: 0

Views: 375

Answers (1)

georgeawg
georgeawg

Reputation: 48968

Remove the $q.deferred anti-pattern:

var functionToTest = function (param) {

  ̶v̶a̶r̶ ̶d̶e̶f̶e̶r̶r̶e̶d̶ ̶=̶ ̶$̶q̶.̶d̶e̶f̶e̶r̶(̶)̶;̶

  return asyncGet(param).then(function (result) {
    //business login involving result

    if (something) 
      return true;
    else
      return false;
  });

  ̶r̶e̶t̶u̶r̶n̶ ̶d̶e̶f̶e̶r̶r̶e̶d̶.̶p̶r̶o̶m̶i̶s̶e̶;̶

}

Also provide an error handler:

  functionToTest(param).then(function (result) {
     expect(result).toBe(true);
     done();
  }).catch(function(err) {
     console.log(err);
     done();
  });

Deferred anti-patterns can hang if the code is written erroneously. Avoid the anti-pattern to prevent this problem.

Include a .catch handler to see any errors.

Upvotes: 0

Related Questions