Fixing unexpected GET request in service unit test - angularjs

In my unit test for a service, I'm testing a GET request. I'm getting the following error, even when I explicitly state $httpBackend.expectGET('/api/example').respond(200, {});.
Error: Unexpected request: GET /api/another/example
Expected GET /api/example
Unit test
describe('MyService', function() {
var MyService,
$httpBackend;
beforeEach(module('example'));
beforeEach(function() {
inject(function(_MyService_, _$httpBackend_) {
MyService = _MyService_;
$httpBackend = _$httpBackend_;
$httpBackend.expectGET('/api/example').respond(200, {});
});
})
describe('#get', function() {
beforeEach(function() {
// this is what i want to test
$httpBackend
.expectGET('/api/another/example')
.respond(200, {});
});
it('should send a get request', function() {
MyService.get();
$httpBackend.flush();
});
});
});

I can't see the content of your MyService.get() function, but I'll go ahead and assume it's calling GET /api/another/example.
If I remember correctly, you have to serve your expected requests on $httpBackend in the order you list them.
Assuming both beforeEach segments get triggered you would have to set off a GET request against /api/example before you can access GET /api/another/example.

Related

Angular unit test factory that uses http request

I am working on an angular js app with karma/Jasmine testing framework, I need to test a factory that returns a http promise but it always return undefined
here is my factory
angular.module('GithubUsers').factory('Users',['$http','$q',function($http,$q){
return{
getAllUsers:function(){
var defered= $q.defer();
$http({
url:'https://api.github.com/users',
method:'GET'
}).then(function(users){
defered.resolve(users.data);
},function(err){
defered.reject(err);
})
return defered.promise;
}
}
}])
here is my tests
Update thanks to your answers I modified my code to the following but no I got this error
Possibly unhandled rejection: {"status":0,"config":{"method":"GET","transformRequest":[null],"transformResponse":[null],"jsonpCallbackParam":"callback","url":"https://api.github.com/users?since=1","headers":{"Accept":"application/json, text/plain, /"},"cached":false},"statusText":""} thrown
describe('Test Users Factory',function(){
var $controller,
Users,
$rootScope,
$httpBackend,
$q;
beforeEach(module('GithubUsers'));
beforeEach(inject(function(_$controller_,_Users_,_$rootScope_,_$httpBackend_,_$q_){
$controller = _$controller_;
Users = _Users_;
$rootScope= _$rootScope_;
$httpBackend=_$httpBackend_;
}))
it('should get users',function(){
var result;
$httpBackend.whenGET('https://api.github.com/users?since=1').respond(function(){
return {data:[{id:2}],status:200};
})
Users.getAllUsers().then(function(res){
result = res;
});
$httpBackend.flush();
$rootScope.$digest()
expect(result).toBeTruthy();
})
})
Thanks in advance!
I think you need to pass a function that returns a array with 3 items in it, to whenGET().respond().
Maybe, you can try something like this:
beforeEach(angular.mock.inject(function (User, $httpBackend, $http) {
...
this.withOKUsers = function() {
var i1 = new User();
i1.id = 10;
return [200, JSON.stringify([ i1]), {}];
} ...
}));
...
it('should get users',function(){
$httpBackend
.whenGET('https://api.github.com/users')
.respond(this.withOKUsers);
Users.getAllUsers().then(function(res){
result = res;
});
$httpBackend.flush();
expect(result).not.toBeNull();
...
(I prefer to arrange spec outside of it() clause for better readability)
You're missing a $httpBackend.flush(); call after your test method call. It will invoke a success/error or then part and resolve a $q's promise properly. For more tests I would move a $httpBackend.whenGET to each test case separately so I can later verify it per use case but it's just my personal opinion.
I find it a little suspicious that you mix a $controller and a factory in one test. I would suggest to split them, and in controller test just check the calls to service methods and in a facotry test itself do a $httpBackend stuff.
Below I paste your test with my corrections. It works now for me:
describe('Test Users Factory', function () {
var Users,
$rootScope,
$httpBackend,
$q;
beforeEach(module('app.utils'));
beforeEach(inject(function (_Users_, _$rootScope_, _$httpBackend_, _$q_) {
Users = _Users_;
$rootScope = _$rootScope_;
$httpBackend = _$httpBackend_;
}));
afterEach(function () {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it('should get users', function () {
var result;
$httpBackend.when('GET', "https://api.github.com/users").respond({ data: [{ id: 2 }], status: 200 });
Users.getAllUsers().then(function (res) {
result = res;
expect(result).toBeTruthy();
});
$httpBackend.flush();
$rootScope.$digest();
});
Important notices:
1)afterEach - check if no pending requests remain after your call
2) your url differ with a parameter ?since=1. But you do not give it as a parameter in your code so i do not understand why you added this parameter.
Maybe consider string concatenation with url and parameter ?

$httpBackend: Angular Unit-Testing that a GET request is sent

I'd like to test that a (component's) controller is sending a GET request to some URL (without caring about the response). I was expecting that
httpBackend.expectGET('/some/random/url');
would spy on the http backend and fail if it did not get the GET request, so I was expecting the following spec to fail:
describe('myApp', function() {
var httpBackend;
beforeEach(module('myApp'));
beforeEach(inject(function($httpBackend) {
httpBackend = $httpBackend;
}));
it('sends a GET request to /some/random/url', function() {
httpBackend.expectGET('/some/random/url');
httpBackend.expect('GET', '/some/random/url');
});
});
But this seems to pass trivially
Starting the Teaspoon server...
Teaspoon running default suite at http://127.0.0.1:56255/teaspoon/default
..
Finished in 0.01200 seconds
2 examples, 0 failures
with this:
angular.module('myApp', []);
So I suppose I am misunderstanding what expectGET is doing and this is not the way the way to check what I am trying to check.
I usually add the following code to any spec (test) files deal with http mocking. This makes sure that the call is flushed and that there are no outstanding expectations / requests.
afterEach(() => {
try {
$httpBackend.flush();
} catch (e) {
}
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
This would change your code like so
describe('myApp', function() {
var httpBackend;
beforeEach(module('myApp'));
beforeEach(inject(function($httpBackend) {
httpBackend = $httpBackend;
}));
afterEach(() => {
try {
httpBackend.flush();
} catch (e) { // entering here is a sign your unit test failed
}
httpBackend.verifyNoOutstandingExpectation();
httpBackend.verifyNoOutstandingRequest();
});
it('sends a GET request to /some/random/url', function() {
httpBackend.expectGET('/some/random/url');
httpBackend.expect('GET', '/some/random/url');
});
});
You forgot to call flush() on httpBackend. That's when it will check that all the expected requests have been received.

Multiple Unexpected Request: GET

I have an angular app, that on initialisation, make a number of http requests.
I have set up a test, to expect the first request, and the second,
describe("MyController--", function () {
var controller, scope, $httpBackend, myFactory;
var iResponse = {
table: 'red',
price: '1.99'
};
beforeEach(angular.mock.module('myApp'));
beforeEach(inject(function (_$controller_, _$rootScope_, _$httpBackend_, _myFactory_) {
scope = _$rootScope_.$new();
$httpBackend = _$httpBackend_;
$httpBackend.expectGET("/app/shopping/cart/layout.html").respond({});
$httpBackend.expectGET("/app/rest/products").respond(iResponse);
myFactory = _myFactory_;
spyOn(myFactory, 'getData').and.callThrough();
controller = _$controller_('MainController', {$scope: scope});
scope.$apply();
$httpBackend.flush();
}));
it('should verify that the controller exists ', function() {
expect(controller).toBeDefined();
});
With the above, i keep seeing the error:
Error: Unexpected request: GET /app/rest/products
Information: Expected GET /app/shopping/cart/layout.html
Any ideas what i am missing?
Firstly I would preload HTML in karma conf so u don't have to expect HTML
Secondly does your controller make a call to the unexpected url? If so u shud expect the request
All the best

karma jasmine servce promise does not resolve in unit test and $stateChangeStart

I need to have created the following unit test that relies on a promise in a service being resolved, but the finally() callback is never called. The promise works just fine in the real application. I have read in various places that I need to kick off a digest cycle but that doesn't work. I'm using ui-router and it just starts an $stateChangeStart request and tries to retrieve the template of the first state. (Hence the $httpBackend mock for that).
var $rootScope;
var scope;
var $httpBackend;
var FormulaValidator;
var mockFunctionApiBaseUrl = 'http://localhost:5555';
beforeEach(function() {
module('ps', function($provide) {
$provide.constant('functionApiBaseUrl', mockFunctionApiBaseUrl);
$provide.value('identity', {
getUsernameFromLocalStorage: function() {
console.log('getting mock username from local storage');
return 'BLAH';
},
verifyToken: function(token) {
return true;
}
});
});
beforeEach(function(done) {
inject(function(_$httpBackend_, _$rootScope_, _FormulaValidator_) {
$httpBackend = _$httpBackend_;
$rootScope = _$rootScope_;
scope = $rootScope.$new();
FormulaValidator = _FormulaValidator_;
$httpBackend.expect('GET', mockFunctionApiBaseUrl + '/api/list/functions').respond(200, '{"MA": {}}');
$httpBackend.expect('GET', '/0.1.1/json/assets.json').respond(200, '["AAPL US EQUITY"]');
$httpBackend.expect('GET', '/null/templates/dashboard.html').respond(200, '<html></html>');
done();
})
});
afterEach(function() {
$httpBackend.flush();
});
it('Basic Validation 1', function (done) {
FormulaValidator.promise.finally(function () {
console.log('FormulaValidator.spec.promise finally');
var p = FormulaValidator.validateFormula('MA(AAPL US EQUITY, 30)');
console.log('getFunctions: ' + FormulaValidator.getFunctions().length);
expect(p).toBe(true);
done();
});
scope.$apply();
//$rootScope.$digest();
});
An $http promise will only be resolved when you flush the $httpBackend.
Flushing it in afterEach() is too late: the point of flushing $httpBackend is to tell it: OK, now you're supposed to have received requests, send back the response so that the promise is resolved with what I've told you to send back when calling $httpBackend.expect().
Read more about it is the doc.

Issue with $httpbackend while testing Angular Controler with Karma-Jasmine

I put the code in a fiddle so it can be easily updated and 'worked with' if needed.
describe('PlayersListCtrl', function() { // Jasmine Test Suite
beforeEach(module('wc2014App'));
var ctrl, scope, $httpBackend;
beforeEach(inject(function($controller, $rootScope) {
scope = $rootScope.$new();
ctrl = $controller('PlayersListCtrl', {
$scope: scope
});
}));
it('should have an empty player array', function() {
expect(scope.players.length).toBe(0);
});
describe('PlayersListCtrl', function() {
var $httpBackend, $rootScope, createController;
beforeEach(inject(function($injector) {
$httpBackend = $injector.get('$httpBackend');
$httpBackend.when('GET', '../app/stubs/players.json').respond(
{userId: 'userX'},
{'A-Token': 'xxx'});
$rootScope = $injector.get('$rootScope');
var $controller = $injector.get('$controller');
createController = function() {
return $controller('PlayersListCtrl', {'$scope' : $rootScope });
};
}));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it('should fetch authentication token', function() {
$httpBackend.expectGET('../app/stubs/players.json');
var controller = createController();
$httpBackend.flush();
});
});
});
The rest, cause its quite verbose, is in the fiddle: http://jsfiddle.net/tLte2/
Basically the first test passes, not a hard one, but the second one depends on a JSON stub and gives errors like:
PhantomJS 1.9.7 (Mac OS X) PlayersListCtrl PlayersListCtrl should fetch authentication token FAILED
Error: No pending request to flush !
Cant seem to get a grip on how this $httpBackend stiff works. Is must be possible to just fire it and set the result in the scope of the controller?
--edit
Basically got everything wired up perfectly and can do some simple tests that run just fine, however getting JSON stub data in there seems to be a pain. Workaround can be just defining the array described in the the JSON on the controller scope like: controller.players = ['one','two','three',..... etc ......]
But that doesnt feel right. That $httpBackend stuff shouldn't be that hard to fix right?

Resources