Verifying a call on $httpBackend in angularjs test - angularjs

I am trying to write a test for angularjs which is mocking $httpBackend. Following is the test.
describe('test', function() {
beforeEach(function (){
module('abcApp');
});
var $httpBackend, filterService;
beforeEach(inject(function($injector) {
$httpBackend = $injector.get('$httpBackend');
filterService = $injector.get('filterService');
}));
it('getCompany calls get on http with correct parameter', function () {
$httpBackend.when('GET', 'api/Abc').respond([]);
filterService.getAbc();
$httpBackend.flush();
expect($httpBackend.get).toHaveBeenCalled();
});
});
when I run the test I get the following error:-
Error: Expected a spy, but got undefined.
Any ideas how I would I assert that $httpBackend.get have been called with required parameter using expect.

expect($httpBackend.get).toHaveBeenCalled();
is invalid. First because $httpBackend doesn't have any (documented) get() method. Second, because $httpBackend.get is not spied by Jasmine, so you can't add Jasmine if this method has been called.
You should just test that, given the empty array returned by the http backend, the service returns the right thing, or has the desired side effect. You could also use
$httpBackend.expectGET('api/Abc').respond([]);
and
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
so that $httpBackend verifies for you that the expected request has been sent.

Related

Unable to use httpBackend flush for ngMockE2E

I am trying to test my controller using jasmine. Basically, when the controller is created it will call a service to make http request. I am using httpBackend to get the fake data. When I try to run the test I always get the error "No pending request to flush". If I remove the httpBackend.flush() then the test fails because controller.data.name is undefined. Can anyone know why it happens like that? Thanks.
The code for the module is here:
var myModule = angular.module('myModule', ['ngMockE2E']);
myModule.run(function($httpBackend){
$httpBackend.whenGET('/Person?content=Manager').respond(function (){
var response = {'name':'Bob','age':'43'}
return [200,response];
})
});
The code for the service:
myModule.factory('myService',function($http){
return {
getData: function(position){
return $http.get('/Person?content='+position);
}
}
});
The code for controller is:
myModule.controller('myController',function(xrefService){
var _this = this;
_this.data ={};
_this.getData = function(position){
myService.getData(position).then(function(response){
_this.data = response.data
});
}
_this.getData("Manager");
})
The code to test the controller is:
describe("Test Controller",function(){
var controller,httpBackend,createController;
beforeEach(module('myModule'));
beforeEach(inject(function($controller,$httpBackend){
createController = function(){
return $controller('myController');
}
httpBackend = $httpBackend;
}));
it("should return data",function(){
controller = createController();
httpBackend.flush();
expect(controller.data.name).toEqual("Bob");
});
})
The angular documentation says the following about $httpbackend for ngMockE2E:
Additionally, we don't want to manually have to flush mocked out
requests like we do during unit testing. For this reason the e2e
$httpBackend flushes mocked out requests automatically, closely
simulating the behavior of the XMLHttpRequest object.
So, short answer: it doesn't exist and you don't need it.
you are using $httpBackend.whenGET inside "The code for the module"
you should be using $httpBackend inside the test code as follows ...
it("should return data",function(){
$httpBackend.expectGET('/Person?content=Manager').respond(function (){
var response = {'name':'Bob','age':'43'}
return [200,response];
})
controller = createController();
httpBackend.flush();
expect(controller.data.name).toEqual("Bob");
});
also i would advise using expectGET instead of whenGET.
With whenGET you are saying if the request is made then response like so.
With expectGET you are saying ... a request will be made, when it is made respond like so, if the request is not made then fail the test.
PS if you put some console.log statements inside your controller code then you should see these log statements when you run your test suite. If not then you know your controller code is not even being hit.
also use ..
afterEach(function () {
httpBackend.verifyNoOutstandingExpectation();
httpBackend.verifyNoOutstandingRequest();
});
which will force test failure if expectations were not met.

$httpBackend.flush() method throws Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting

I am trying to unit test my AngularJS application using Karma and Jasmine. I want to mock the $http service. For that, I am using the $httpBackend method. Below is my service that I want to test:
angular.module('MyModule').factory('MyService', function($http, $log, $parse, $q, $timeout, $filter, MyOtherService1, MyOtherService2){
var service = {};
service.getSomething = function(id){
return $http.get('/somePath/subpath/' + id);
}
});
My unit test for this service is:
describe("myTest", function(){
var myService, $httpBackend, scope, mockMyOtherService1, mockMyOtherService2;
var myResponse =
{
foo:'bar'
};
beforeEach(module("MyModule"));
beforeEach(inject(function(_MyService_, $injector){
$httpBackend = $injector.get('$httpBackend');
myService = _MyService_;
scope = $injector.get('$rootScope').$new();
mockMyOtherService1 = $injector.get('MyOtherService1');
mockMyOtherService2 = $injector.get('MyOtherService2');
}));
beforeEach(function(){
//To bypass dependent requests
$httpBackend.whenGET(/\.html$/).respond(200,'');
});
//If I uncomment the below afterEach block, the same error is shown at next line.
/*afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});*/
//This test passes successfully
it("should check if service is instantiated", function () {
expect(myService).toBeDefined();
});
//This test passes successfully
it("should expect dependencies to be instantiated", function(){
expect($httpBackend).toBeDefined();
});
//The problem is in this test
it("should get the getSomething with the provided ID", function() {
$httpBackend.whenGET('/somePath/subpath/my_123').respond(200,myResponse);
var deferredResponse = myService.getSomething('my_123');
//The error is shown in next line.
$httpBackend.flush();
//If I comment the $httpBackend.flush(), in the next line, the $$state in deferredResponse shows that the Object that I responded with is not set i.e. it does not matches the 'myResponse'.
expect(deferredResponse).toEqual(myResponse);
});
});
This is an emergency problem and I need help regarding the same as soon as possible. I will be very grateful for your answer.
The problem was I needed to inject $location in my spec files even though they are not injected in the services. After injection, all worked well! Hope this helps someone who gets stuck in the same situation.
You will get a promise from your service. So change your test code to:
//The problem is in this test
it("should get the getSomething with the provided ID", function (done) {
$httpBackend.expectGET('/somePath/subpath/my_123').respond(200,myResponse);
var deferredResponse = myService.getSomething('my_123');
deferredResponse.then(function (value) {
expect(value.data).toEqual(myResponse);
}).finally(done);
$httpBackend.flush();
});
I've recently had this problem when updating a project from Angular 1.2 to 1.4. The test code looked something like:
it('should call /something', function(){
httpBackend.expectGET('/something').respond(200);
scope.doSomething();
httpBackend.flush();
});
The error was the infdig past 10 iterations. It was caused by invoking the .flush() method. I figured out this is seemingly because there were no pending promises created within doSomething().
Once I added a promise somewhere within doSomething() or inner methods, the infdig problem went away.
I suspect - and this is 100% speculation so don't let it influence your development - this is because httpBackend does some trickery to wait for promises, which maybe involves digesting repeatedly until there's a change. Since there's no promises, there's no changes - infinite digest.

How to test an error response in angular's $httpBackend?

We are using angular 1.2.x (we have to due to IE8). We are testing with Karma and Jasmine. I want to test the behavior of my modules, in case the server responds with an error. According to the angular documentation, I should just simply prepare the $httpBackend mock like this (exactly as I'd expect):
authRequestHandler = $httpBackend.when('GET', '/auth.py');
// Notice how you can change the response even after it was set
authRequestHandler.respond(401, '');
This is what I am doing in my test:
beforeEach(inject(function($injector) {
keepSessionAliveService = $injector.get('keepSessionAliveService');
$httpBackend = $injector.get('$httpBackend');
$interval = $injector.get('$interval');
}));
(...)
describe('rejected keep alive request', function() {
beforeEach(function() {
spyOn(authStorageMock, 'get');
spyOn(authStorageMock, 'set');
$httpBackend.when('POST', keepAliveUrl).respond(500, '');
keepSessionAliveService.start('sessionId');
$interval.flush(90*60*1001);
$httpBackend.flush();
});
it('should not add the session id to the storage', function() {
expect(authStorageMock.set).not.toHaveBeenCalled();
});
});
But the test fails, because the mock function is being called and I can see in the code coverage that it never runs into the error function I pass to the §promise.then as second argument.
Apparently I am doing something wrong here. Could it have to with the older angular version we're using?
Any help would be appreciated!
Something like this:
it("should receive an Ajax error", function() {
spyOn($, "ajax").andCallFake(function(e) {
e.error({});
});
var callbacks = {
displayErrorMessage : jasmine.createSpy()
};
sendRequest(callbacks, configuration);
expect(callbacks.displayErrorMessage).toHaveBeenCalled();

Where should $httpBackend.expect() go?

The following test passes:
admin.controller.js
angular
.module('mean-starter')
.controller('AdminController', AdminController);
function AdminController(User, Auth, $state) {
var vm = this;
User
.list()
.success(function(data) {
vm.users = data;
})
.error(function() {
console.log('Problem getting users.');
});
vm.delete = function(id) {
User
.delete(id)
.success(function(data) {
if (Auth.getCurrentUser()._id === id) Auth.logout(); // deleting yourself
else $state.reload();
})
.error(function() {
console.log('Problem deleting user.');
});
};
}
admin.controller.spec.js
describe('AdminController', function() {
var AdminController, $httpBackend;
beforeEach(module('mean-starter'));
beforeEach(module('templates'));
beforeEach(inject(function($controller, $rootScope, _$httpBackend_) {
$httpBackend = _$httpBackend_;
AdminController = $controller('AdminController');
}));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it('gets users', function() {
$httpBackend
.expectGET('/users')
.respond('foo');
$httpBackend.flush();
});
});
I wouldn't expect it to. Here is what I expected to happen:
The controller is instantiated in the beforeEach.
User.list() gets run.
The $http isn't yet overridden by $httpBackend, so the request goes out normally.
$httpBackend.expectGET('/users').respond('foo') expects GET /users. And says, "I'll respond with 'foo' if I get that request".
$httpBackend.flush() says "Send out the defined responses for any of the requests that $httpBackend received."
.expectGET fails because it doesn't receive it's request (the request happened before the expectation).
.flush() throws an error because there's nothing to flush.
I'm not getting the outcomes I was expecting, so something about my logic above must be wrong - what is it?
The $http isn't yet overridden by $httpBackend, so the request goes out normally.
This is not a correct assumption. $httpBackend is used automatically in unit tests (it's part of the ng-mocks module that's used in unit tests). So whether you use $httpBackend or not in your unit test code, it's there and it's processing all the $http requests that your code makes.
Think about it, if it wasn't doing this, your unit tests could make real requests.
EDIT
For cases like this, where the controller is making an HTTP request as soon as it's instantiated, I like to put the $httpBackend.expectGET() call in the beforeEach block right before you instantiate the controller.
I would also flush() the backend in the beforeEach block as well. This, I think, makes it clear that these requests happen at controller startup. And it means I don't have to make this expectation in every unit test.

Unit testing a factory with angularjs and acute reaching the API

I want to unit test a $resource but actually reach the REST API and get it's respone. So, I don't want to mock it.
I am very new to unit testing and here is what I did:
describe('Factory: PartnersResource', function () {
var PartnersResource,
$httpBackend;
beforeEach(module('app'));
beforeEach(inject(function (_PartnersResource_, _$httpBackend_) {
PartnersResource = _PartnersResource_;
$httpBackend = _$httpBackend_;
}));
it('when post, it should query all partners and return them', function () {
var result = PartnersResource.query();
$httpBackend.flush();
});
});
It does not work obviously :)
Error: Unexpected request: GET https://www-dev.mysite.com/film/partner
No more request expected
The resource works, normally but it won't in this unit test.
I use ngMocks to simulate a fake back-end for modules of the app I am working, ahead of the API.
I have setul the mocks to allow calls to my url:
$httpBackend.whenGET(/www-dev/).passThrough();
Can anyone help?

Resources