No pending request to flush on using whenGET, but not expectGET - angularjs

When testing an angular service using $httpBackend I get some unexpected results.
When I run the test using httpBackend.expectGET the test works as expected. However if I run the exact same test using whenGET the test fails with the message 'No pending request to flush'.
Here is the service under test:
.factory('CoverageService', ['$http', function ($http) {
return{
GetCoverageReport: function () {
return $http.get('../../../js-test-reports/coverage/Chrome 43.0.2357%20(Windows%207)/cobertura-coverage.xml');
},
Here are the tests:
'use strict';
describe('coverage-manager.js', function () {
describe('CoverageService', function () {
var httpBackend, sut, rootScope;
beforeEach(module('fot'));
describe('GetCoverageReport', function () {
beforeEach(inject(function ($httpBackend, CoverageService, $rootScope) {
httpBackend = $httpBackend;
sut = CoverageService;
rootScope = $rootScope;
}));
it('should return data', function () {
var expectedData = { testData: 'some data' };
var actualData;
httpBackend.expectGET('../../../js-test-reports/coverage/Chrome 43.0.2357%20(Windows%207)/cobertura-coverage.xml').respond(expectedData);
sut.GetCoverageReport()
.success(function (response) { actualData = response; });
httpBackend.flush();
expect(actualData).toEqual(expectedData);
});
it('should return data but says No pending request to flush', function () {
var expectedData = { testData: 'some data' };
var actualData;
httpBackend.whenGET('../../../js-test-reports/coverage/Chrome 43.0.2357%20(Windows%207)/cobertura-coverage.xml').respond(expectedData);
sut.GetCoverageReport()
.success(function (response) { actualData = response; });
rootScope.$digest();
httpBackend.flush();
expect(actualData).toEqual(expectedData);
});
it('should return data', function () {
var expectedData = { testData: 'some data' };
var actualData;
httpBackend.whenGET('../../../js-test-reports/coverage/Chrome 43.0.2357%20(Windows%207)/cobertura-coverage.xml').respond(expectedData);
sut.GetCoverageReport()
.success(function (response) { actualData = response; });
httpBackend.flush();
expect(actualData).toEqual(expectedData);
});
});
});
});
The expectGET passes, both whenGETs fail with "No pending request to flush" message. I tried using the $rootScope.$digest() as I saw in some posts, no luck. The tests are identical except the expectGET vs expectWHEN, so not sure what is going on?
And here is the error detail:
Error: No pending request to flush !
at Function.$httpBackend.flush (file:///C:/dev/fot/git/client/src/main/js/bower_components/angular-mocks/angular-mocks.js:1455:34)
at Object.<anonymous> (file:///C:/dev/fot/git/client/src/test/javascript/spec/controllers/devTools/CoverageManager.spec.js:78:21)
at attemptSync (file:///C:/dev/fot/git/client/src/main/js/node_modules/karma-jasmine/lib/jasmine.js:1510:12)
at QueueRunner.run (file:///C:/dev/fot/git/client/src/main/js/node_modules/karma-jasmine/lib/jasmine.js:1498:9)
at QueueRunner.execute (file:///C:/dev/fot/git/client/src/main/js/node_modules/karma-jasmine/lib/jasmine.js:1485:10)
at Spec.queueRunnerFactory (file:///C:/dev/fot/git/client/src/main/js/node_modules/karma-jasmine/lib/jasmine.js:518:35)
at Spec.execute (file:///C:/dev/fot/git/client/src/main/js/node_modules/karma-jasmine/lib/jasmine.js:306:10)
at Object.<anonymous> (file:///C:/dev/fot/git/client/src/main/js/node_modules/karma-jasmine/lib/jasmine.js:1708:37)
at attemptAsync (file:///C:/dev/fot/git/client/src/main/js/node_modules/karma-jasmine/lib/jasmine.js:1520:12)
at QueueRunner.run (file:///C:/dev/fot/git/client/src/main/js/node_modules/karma-jasmine/lib/jasmine.js:1496:16)
angular 1.2.28
angular-mocks 1.2.28
jasmine 2.0.0

Related

testing a service's call to $http with $httpBackend

I have an AngularJS service for a restful API:
angular
.module('app', [
])
.service('api', ['$http', '$q', function APIService($http, $q) {
this.get = function (dataProperty, params) {
return $http({
method: 'get',
url: 'https://some.api/rest/',
params: angular.extend({
default_params...
}, params)
})
.then(
function (result) {
if (result.data.status === 'ok') {
return result.data[dataProperty];
} else {
return $q.reject(angular.extend(new Error(result.data.message), { result: result.data }));
}
},
function (reason) {
return $q.reject(angular.extend(new Error('AJAX request to the API failed'), { reason: reason.data }));
});
};
}]);
I'm trying to test this api.get with the following:
describe('api', function () {
var
$httpBackend,
service;
beforeEach(module('app'));
beforeEach(inject(function (_$httpBackend_, _api_) {
$httpBackend = _$httpBackend_;
service = _api_;
}));
afterEach(function () {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it('', function () {
$httpBackend
.when('get', 'https://some.api/rest/')
.respond({
data: {
status: 'ok'
}
});
service.get('status', {});
$httpBackend.flush();
$httpBackend
.expect('get', 'https://some.api/rest/');
});
});
But I'm getting the error callback every time:
Error: AJAX request to the API failed in bower_components/angular-mocks/angular-mocks.js (line 279)
Am I going about setting up the test correctly? I believe the .when and .response is used to fake the actual $http call, but I can't get the success callback to fire.
The two issues were .when not looking for the correct URL (because get params were thrown in I needed to make it a regex:
.when('GET', /https:\/\/api\.flickr\.com\/services\/rest\/.*/)
Then, the .respond doesn't need to be padded with a data object, it does that for you:
.respond({ stat: 'ok' });

How to write a test unit for a service that returns a promise

Here is my factory in my app.js
app.factory('userInfoFacrory', ['$http' , "$q", function($http,$q){
return {
getNames:function(){
var differed = $q.defer();
$http.get("http://localhost/ang/api/v1/users/names")
.success(function(data) {
differed.resolve(data);
}).error(function(msg) {
differed.reject(msg);
});
return differed.promise;
}
}
}])
I use this factory in my controller like bellow , and it works fine :
app.controller('mainController', ['$scope','userInfoFacrory','$log', function($scope,userInfoFacrory,$log){
var promise = userInfoFacrory.getNames();
promise.then(function (data) {
$log.info(data); // I get my data correctly here
}, function (msg) {
$log.error(data);
})
}])
And here , I've tried to write a test unit , with karma-jasmine
describe('userInfoFacrory', function() {
var factory ,$rootScope,$scope,$q,onTaskComplete , promise;
beforeEach(function() {
module("testApp");
inject(function ($injector) {
$q = $injector.get("$q");
factory = $injector.get("userInfoFacrory");
$rootScope = $injector.get("$rootScope");
$scope = $rootScope.$new();
promise = factory.getNames(); // this function comes from my factory which returns a promise
});
});
it('should return a promise', function() {
// This test will pass , so no error so far
expect(typeof promise.then).toEqual('function');
});
});
But I can't figure out how to test to so if my promise will have my data ( that comes from my api ) or not , any suggestion would be appreciated.
thanks
it('should return a promise resolved with the http response data if the http request is successful', inject(function($httpBackend) {
var expectedData = 'fake data';
$httpBackend.expectGET('http://localhost/ang/api/v1/users/names').respond(expectedData);
var promise = factory.getNames();
var actualData;
promise.then(function(result) {
actualData = result;
});
$httpBackend.flush();
expect(actualData).toEqual(expectedData);
}));
it('should return a promise rejected with the http response data if the http request is in error', inject(function($httpBackend) {
var expectedData = 'fake data';
$httpBackend.expectGET('http://localhost/ang/api/v1/users/names').respond(400, expectedData);
var promise = factory.getNames();
var actualData;
promise.catch(function(result) {
actualData = result;
});
$httpBackend.flush();
expect(actualData).toEqual(expectedData);
}));
Working plunkr: http://plnkr.co/edit/NfO6KXWLs1QT5HG8MK0J?p=preview
Note that your code is correct, but doesn't really leverage the chaining capabilities of promises. It could simply be written as
getNames: function() {
return $http.get("http://localhost/ang/api/v1/users/names")
.then(function(response) {
return response.data;
}, function(response) {
return $q.reject(response.data);
});
};
}
Working plunkr: http://plnkr.co/edit/C5x8wRYCQ0wetjozEd0a?p=preview

how we do spyOn with a function referred in a async call

Need help on Jasmine with angularjs :
I created spec for this below angular code. I could able to work with the service, if i don't have a data1 function. I need to know how we do spyOn with a function referred in a async call.
'use strict';
angular.module('myApp.services', [])
.factory("exampleService", function ($http) {
var data1 = function () {
return '/test';
}
return {
data: function () {
return data1();
},
getData: function () {
return $http.get("/exampleUrl" + data1());
}
}
});
Below spec code is
'use strict';
describe('service', function () {
var $httpBackend;
beforeEach(module('myApp.services'));
beforeEach(inject(function ($injector) {
$httpBackend = $injector.get("$httpBackend");
$httpBackend.when("GET", "/exampleUrl/test1")
.respond(200, {
value: "goodValue"
});
}));
afterEach(function () {
$httpBackend.flush();
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
describe('exampleService successful http request', function () {
it('.value should be "goodValue"', inject(function (exampleService) {
spyOn(exampleService, 'data').and.callFake(function () {
//done();
return '/test1';
});
exampleService.getData().success(function (response) {
expect(response.value).toEqual("goodValue");
}).error(function (response) {
//should not error with $httpBackend interceptor 200 status
expect(false).toEqual(true);
});
}));
});
});
But i am getting exception like this
Error : Unexpected Request : GET /exampleUrl/test

angularjs http service unit testing

I am trying to test a simple service for learning purposes..However; I can't figure out how it must be done:
service:
.factory('myService', function($http) {
var myService = {
async: function() {
var promise = $http.get('test.json').then(function (response)
{
return response.data;
});
return promise;
}
};
return myService;
});
controller:
myService.async().then(function(d) {
$scope.data = d;
$scope.e = $scope.data.txt;
});
test:
'use strict';
describe("myService", function(){
beforeEach(module("testingExpApp"));
var myService,
$httpBackend;
beforeEach(inject(function(myService, _$httpBackend_){
myService = myService;
$httpBackend = _$httpBackend_;
}));
describe("get", function(){
it('should return test.json data', function () {
var url = "../mock/test.json";
var x = $httpBackend.expectGET(url).respond(200, 'txt from json');
// flush response
$httpBackend.flush();
expect(x).toBe('txt from json');
});
});
});
I get 'no pending request to flush!'
I just want to test that myservice.get() get the test.json file data..I have tried everything but can't get it working..
Any tips?
Thanks a lot in advance!
What I was missing is to call service function
it had to be:
it('should return test.json data', function () {
var url = "../../mock/test.json";
$httpBackend.expectGET(url).respond(200, 'data from test.json');
//Execute service func here..
myServiceFunc.async().then(function(result) {
console.log(result);
expect(result).toEqual('data from test.json');
});
$httpBackend.flush();
});

Angular Jasmine test response interceptor

I'm trying to test my response interceptor but I have a hard time figuring out how to mock the $window object. Here is my interceptor code :
'use strict';
angular.module('Domain.handlers')
.config(function($httpProvider) {
$httpProvider.responseInterceptors.push('UnauthorizedInterceptor');
})
.factory('UnauthorizedInterceptor', function($q, $injector, $window, ENV) {
return function(promise) {
var success = function(response) { return response; };
var error = function(response) {
if (response.status === 401) {
$window.location.href = ENV.account + '/oauth/authorize?client_id=' + ENV.clientId + '&redirect_uri=' + ENV.app + '/oauth/callback&response_type=token';
}
return $q.reject(response);
};
return promise.then(success, error);
};
});
And here is my spec :
'use strict';
describe('Domain.handlers.response', function() {
var UnauthorizedInterceptor,
httpProvider,
$httpBackend,
$http,
token = '123456789';
beforeEach(module('Domain.handlers', function($httpProvider) {
httpProvider = $httpProvider;
}));
beforeEach(inject(function(_UnauthorizedInterceptor_, _$httpBackend_, _$http_) {
UnauthorizedInterceptor = _UnauthorizedInterceptor_;
$httpBackend = _$httpBackend_;
$http = _$http_;
}));
describe('UnauthorizedInterceptor', function() {
it('should be defined', function() {
expect(UnauthorizedInterceptor).toBeDefined();
});
describe('HTTP status', function() {
describe('is 200 OK', function() {
it('should return a 200 status', function() {
$httpBackend.expectGET('http://api.domain.com/clients').respond(200, {});
$http.get('http://api.domain.com/clients');
$httpBackend.flush();
});
});
describe('is 401 Unauthorized', function() {
it('should redirect to accounts.domain.com', inject(function($window) {
$httpBackend.expectGET('http://api.domain.com/clients').respond(401, {});
$http.get('http://api.domain.com/clients');
expect($window.location.href).toEqual('http://accounts.domain.com/oauth/.....');
$httpBackend.flush();
}));
});
});
});
});
I've got a : Expected 'http://localhost:8080/context.html' to equal 'http://accounts.domain.com/oauth/.....'. Any help on how to mock properly the $window object or more generally how to test a 401 + redirection case?
You should structure your interceptor definition using the more recent syntax. Your URL construction should also be in a service so that it can easily be mocked in tests.
.factory('UnauthorizedInterceptor', function($q, $window, OtherService) {
var service = {
responseError: handleUnauthorized
};
return service;
function handleUnauthorized(rejection) {
if (rejection.status === 401) {
$window.location.href = OtherService.getUnauthorizedRedirectURL();
}
return $q.reject(rejection);
}
});
Doing so will let you test it just like any other factory without having to worry about the internal implementations of $http interceptors, or having to mock responses with $httpBackend.
describe('Domain.handlers.response', function() {
var $window,
UnauthorizedInterceptor,
OtherService,
redirectUrl = 'someUrl';
beforeEach(module('Domain.handlers'));
beforeEach(function () {
$window = { location: { href: null } };
module(function($provide) {
$provide.value('$window', $window);
});
});
beforeEach(inject(function(_UnauthorizedInterceptor_, _OtherService_) {
UnauthorizedInterceptor = _UnauthorizedInterceptor_;
OtherService = _OtherService_;
spyOn(OtherService, 'getUnauthorizedRedirectURL').andReturn(redirectUrl);
}));
describe('UnauthorizedInterceptor', function() {
it('should be defined', function() {
expect(UnauthorizedInterceptor).toBeDefined();
});
it('should have a handler for responseError', function () {
expect(angular.isFunction(UnauthorizedInterceptor.responseError)).toBe(true);
});
describe('when HTTP 401', function () {
beforeEach(function () {
var rejection = { status: 401 };
UnauthorizedInterceptor.responseError(rejection);
});
it('should set window location', function () {
expect($window.location.href).toBe(redirectUrl);
});
});
describe('when not HTTP 401', function () {
beforeEach(function () {
var rejection = { status: 500 };
UnauthorizedInterceptor.responseError(rejection);
});
it('should not set window location', function () {
expect($window.location.href).not.toBe(redirectUrl);
});
});
});
});
Here is an example of the responseError interceptor and the corresponding jasmine spec.
angular.module('interceptorDemo').factory('redirectInterceptor', ['$q', '$window', function($q, $window) {
'use strict';
function handleUnauthorizedAccess(config) {
if (401 === config.status) {
$window.location = '/signIn/';
}
return $q.reject(config);
}
return {
responseError: handleUnauthorizedAccess
};
}]);
The interceptor intercepts the ajax request, if the request is failed, then if the status code is 401 then user is redirected to signIn page.
Jasmine spec for the same is:
describe('redirectInterceptor specs', function() {
var redirectInterceptor, $q;
beforeEach(module('interceptorDemo'));
beforeEach(function() {
$window = {
location: {
href: null
}
};
module(function($provide) {
$provide.value('$window', $window);
});
});
beforeEach(inject(function(_redirectInterceptor_, _$q_) {
redirectInterceptor = _redirectInterceptor_;
$q = _$q_;
spyOn($q, 'reject');
}));
describe('redirectInterceptor specs', function() {
it('should redirect to signIn page for unauthorized access', function() {
var response = {
status: 401,
config: {}
};
var promise = redirectInterceptor.responseError(response);
expect($window.location).toBe('/singIn/');
expect($q.reject).toHaveBeenCalled();
});
it('should not redirect to signIn page for error code other than unauthorized access', function() {
var response = {
status: 404,
config: {}
};
var promise = redirectInterceptor.responseError(response);
expect($window.location).toEqual({
href: null
});
expect($q.reject).toHaveBeenCalled();
});
});
});
We have spied on the $q so we can also test that the reject is called for the 401 error.

Resources