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?
答案 0 :(得分:1)
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.
I hope this helps! Have a nice day!
答案 1 :(得分:1)
One approach is to add expected calls to the httpBackend like this:
var $httpBackend;
beforeEach(function () {
inject(function ( _$httpBackend_ ) {
$httpBackend = _$httpBackend_;
}
....
it('....', function(){
//before the rest of the test
$httpBackend.expectGET('url').respond("OK");
});
https://docs.angularjs.org/api/ngMock/service/$httpBackend
So, your code will look like this:
describe('simple directive', function() {
var $http, $timeout, scope, MyFactory, $httpBackend;
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_, _$httpBackend_) {
$httpBackend = _$httpBackend_;
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);
$httpBackend.expectGET("mocked-get-path?id=elem1").respond("OK");
MyFactory.myGet('elem1')
.then(function(res) {
expect($http.get).toHaveBeenCalledWith(ROUTES.get);
done();
});
$timeout.flush();
});
});
It will get you passed the unexpected GET error. But then there is another issue about not getting the call within the timeout period.
And, providing that now you are expecting a correct call at the $httpBackend, you can simplify your test case by removing the async nature of it like this:
(remove the paremeter done, and simplify the test code)
it('should use ROUTES.get on method myGet', function() {
spyOn(Math, "random").and.returnValue(0.01);
$httpBackend.expectGET("mocked-get-path?id=elem1").respond("OK");
MyFactory.myGet('elem1');
$timeout.flush();
});