Waldir J. Pereira Junior Waldir J. Pereira Junior - 1 month ago 8
AngularJS Question

How to test an AngularJS factory method that returns a $timeout promise with a $http.get inside?

For specific purposes, I had to write a factory method that returns a

$timeout
promise that, inside of it, returns a
$http.get
promise.

I want to test if a call to my factory method will call the
$http.get
with the correct mocked URL (
mocked-get-path
).

Here is my factory code:

(function() {
'use strict';

angular.module('MyApp', []);

angular.module('MyApp')
.constant("ROUTES", {
get: "real-get-path"
});

angular.module('MyApp')
.factory('MyFactory', ['$http', '$timeout', 'ROUTES', MyFactory]);

function MyFactory($http, $timeout, ROUTES) {
return {
myGet: function(id) {
var random = Math.random() * 1000;
return $timeout(function () {
return $http.get(ROUTES.get, {
params: { id: id }
})
.then(function() {
return response.data;
});
}, random);
}
};
}
})();


And my test specification:

describe('simple factory test', function() {
var $http, $timeout, scope, MyFactory;

var ROUTES = {
get: 'mocked-get-path'
};

beforeEach(module('MyApp', function ($provide) {
$provide.constant('ROUTES', ROUTES);
}));

beforeEach(module('MyApp'));

beforeEach(inject(function(_$rootScope_, _$http_, _$timeout_, _MyFactory_) {
scope = _$rootScope_.$new();
$http = _$http_;
$timeout = _$timeout_;
MyFactory = _MyFactory_;
}));

it('should use ROUTES.get on method myGet', function(done) {
spyOn(Math, "random").and.returnValue(0.01);

MyFactory.myGet('elem1')
.then(function(res) {
expect($http.get).toHaveBeenCalledWith(ROUTES.get);
done();
});
$timeout.flush();
});
});


You can see that I tried to write a
expect
for the
$http.get
inside the
then
, but it didn't work.

I receive this error from Jasmine:


Error: Unexpected request: GET mocked-get-path?id=elem1


I made a Plunker: https://plnkr.co/edit/Ia6Q6GvKZOkNU2B8GrO1

What am I doing wrong?

KGJ KGJ
Answer

When testing $http you are going to want to use $httpBackend.when

In your case:

it('should use ROUTES.get on method myGet', function(done) {
  spyOn(Math, "random").and.returnValue(0.01);
  spyOn($http, "get").and.callThrough();

  MyFactory.myGet('elem1')
    .then(function(res) {
      expect($http.get).toHaveBeenCalledWith(ROUTES.get, {params: {id: 'elem1'}});
      done();
    });

  $httpBackend
    .when('GET', "mocked-get-path?id=elem1")
    .respond(200, { foo: 'bar' });

  $timeout.flush(100);

  $timeout.verifyNoPendingTasks();
  $httpBackend.flush();

});

This will cause $httpBackend to complete your request successfully allowing your .then with the expect to execute.

Plunkr showing solution

ngmock fundamentals

I hope this helps! Have a nice day!