How to call $http request [duplicate] - angularjs

This question already has an answer here:
Unexpected request: GET No more request expected at $httpBackend
(1 answer)
Closed 7 years ago.
I am trying to make a demo of $http request and test that service also..I do like this
.controller('cntrl',function($scope,appfactory,$http){
$scope.data=[];
appfactory.setValue('test abc');
$scope.getData=function(){
$http.get('data.json').success(function(data){
console.log(JSON.stringify(data))
$scope.data=data;
}).error(function(data){
console.log(JSON.stringify(data))
})
}
$scope.getData();
$scope.toggle=function(){
if(appfactory.mode()=='a'){
$scope.message='naveen'
}else {
if(appfactory.mode()=='b'){
$scope.message='parveen'
}
}
}
})
I load that data from json file and display it on view..But when I try to test that application ..I got error why ? how to remove that error
beforeEach(inject(function($rootScope,$controller,appfactory,_$httpBackend_) {
$scope = $rootScope.$new();
$httpBackend=_$httpBackend_;
createController = function() {
return $controller('cntrl', {'$scope' : $scope });
};
}));
it("tracks that the spy was called", function() {
var response=[{"name":"naveen"},{"name":"parveen"}]
$httpBackend.expectGET('data.json').respond(response);
var controller = createController();
$scope.getData();
$httpBackend.flush();
expect($scope.data[0].name).toEqual('naveen')
});
})
here is my code
http://plnkr.co/edit/zdfYdtWbnQz22nEbl6V8?p=preview

http call to get data is effectively getting called twice in the test. One is when the controller is initialized (because you're calling $scope.getData()) and the second is when you manually call $scope.getData() in the spec.
And your expectation is only mocked once (before controller initialisation). To fix the test (to mimic what the controller is actually doing), the expectation has to be set once more before $scope.getData() is called.
See plunker: http://plnkr.co/edit/eLPOZTCgkSfoACLJsVZo?p=preview
OR
You can replace the expectGET with whenGET which doesn't fail the test if the resource is not called. BUT since you're expecting expect($scope.data[0].name).toEqual('naveen') where 'naveen' comes from the http respose, that change should be fine.

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.

how to remove Unexpected request: GET data.json?

I am trying to use $httpBackend to test my $http request ..i AM getting
this error
Unexpected request: GET data.json
No more request expected
here is my testing code
beforeEach(inject(function($rootScope,$controller,appfactory,_$httpBackend_) {
$scope = $rootScope.$new();
$httpBackend=_$httpBackend_;
ctrl = $controller('cntrl', {$scope: $scope});
fac=appfactory;
modeSpy= spyOn(fac, 'mode').and.returnValue('a');
}));
it('test true value',function(){
expect(true).toBeTruthy()
})
it('check message value',function(){
$scope.toggle();
expect($scope.message).toEqual('naveen')
})
it("tracks that the spy was called", function() {
//expect(fac.setValue).toHaveBeenCalled();
var response=[{"name":"naveen"},{"name":"parveen"}]
$httpBackend.expectGET('data.json').respond(response);
$scope.getData();
$httpBackend.flush();
expect($scope.data[0].name).toEqual('naveen')
});
here is my code
http://plnkr.co/edit/zdfYdtWbnQz22nEbl6V8?p=preview
Solution to remove this error
I have this controller
.controller('cntrl',function($scope,appfactory,$http){
$scope.data=[];
appfactory.setValue('test abc');
$scope.getData=function(){
$http.get('data.json').success(function(data){
console.log(JSON.stringify(data))
$scope.data=data;
}).error(function(data){
console.log(JSON.stringify(data))
})
}
$scope.getData();
$scope.toggle=function(){
if(appfactory.mode()=='a'){
$scope.message='naveen'
}else {
if(appfactory.mode()=='b'){
$scope.message='parveen'
}
}
}
})
if i comment out this line $scope.getData(); or remove this line $scope.getData(); then my test is pass and I am able to remove this error .
My Question why this error occurred? If am not able to use the function in controller then what is the use of testing of this function ?
When initialising the controller because you're calling $scope.getData(), data.json will be fetched. So, when initialising the controller in test, that request would have to be mocked every time controller is initialised.
One way to overcome this is to add ng-init="getData()" to the html element using this controller, so that the data would be fetched but only on request by the html and not when controller is initialized.

$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.

Testing methods returning promise using jasmine

I am using jasmine & karma to test my angular app. I have a service as follows
app.service('demo1', function( $http ){
this.send = function(){
return $http({
url: 'someurl'
});
}
});
The response is mocked using ngMockE2E.
My jasmine spec is as follows:
describe('Testing asynchronus', function(){
var demoService;
beforeEach(function(){
module('app');
inject(function( demo1 ){
demoService = demo1
});
});
it('Should be able to test promise', function(){
demoService.send().then(function( data ){
expect(data.status).toBe(true);
});
});
});
Now the problem is, the expect is not executing. the test being passed every time, no matter what the value of data.status is. I need help on how to test these kinds of scenarios? Thanks in advance.
Real code:
describe("Testing MetaService", function(){
var _entityMeta_, metaService, scope;
beforeEach(function(){
console.log( '---------------------- Starting Meta Service fetchEntityMeta Test ---------------------------' );
module(APP_MODULE_NAME);
inject(function(_entityMeta_, _metaService_, $rootScope){
metaService = _metaService_;
entityMeta = _entityMeta_;
scope = $rootScope.$new();
});
});
afterEach(function(){
console.log( '---------------------- Ending Meta Service fetchEntityMeta Test ---------------------------' );
});
// Giving mock data from entityMeta.person as input
it("Should have a valid structure", function($rootScope){
console.log( '////////////////////////////////////////////' );
metaService.fetchEntityMeta('person').then(function( data ){
console.log( data );
expect(data.type).toBe('object');
expect(data.properties.length).toBeGreaterThan(0);
expect(data.definitions.length).toBeGreaterThan(0);
});
});
});
I am getting following error:
Error: Timeout - Async callback was not invoked within timeout specified
by jasmine.DEFAULT_TIMEOUT_INTERVAL.
try to add
beforeEach(module('app'));
under the describe
If you're using Jasmine 2, the argument to the callback passed to it is supposed to be a done function called when the test finished. (The test runner will time out if it is never called.)
(See the official doc here, or one of many blog posts.)
I don't really understand why you pass $rootScope in this case, but the error message looks like a complain because the done function (which happens to be named $rootScope here) has not be called.
This might work :
// Giving mock data from entityMeta.person as input
it("Should have a valid structure", function(done){
console.log( '////////////////////////////////////////////' );
metaService.fetchEntityMeta('person').then(function( data ){
console.log( data );
expect(data.type).toBe('object');
expect(data.properties.length).toBeGreaterThan(0);
expect(data.definitions.length).toBeGreaterThan(0);
done();
}, function (e) {
// The promise was not resolved, this is most likely an
// implementation error. Let's fail the test !
throw new Error(e);
});
});
If the then part is never called, the done function will not either, an you'll get the timeout error you're seeing right now.

AngularJS "No pending request to flush" while unit testing a controller with a $resource

I am writing unit tests for a controller. This controller has a $resource service injected :
function controller($scope, Service) {
Service.get(function(result){
// do stuff with the result, not relevant here
}
}
The service is defined like this :
angular.module('so').factory('Service', ['$resource', service]);
function service($resource) {
return $resource('/url', null, {
get: { method: 'POST', params: {}, isArray: false}
});
}
My Jasmine unit test is the following :
describe("Controller", function(){
var $httpBackend;
beforeEach(function() {
module('so');
inject(function( _$httpBackend_) {
$httpBackend = _$httpBackend_;
});
});
it('should have done stuff irrelevant to the question', function() {
var $injector = angular.injector('so'),
$scope = $injector.get('$rootScope'),
$httpBackend
.whenPOST('/url')
.respond ([]);
// controller needs to be defined here and not in the beforeEach as there
// are more parameters passed to it, depending on the test
var controller = $injector.get('$controller')('controller', { "$scope": $scope });
$httpBackend.flush();
// then here the actual test resolution, also irrelevant
});
});
I get an error when running the test :
Error: No pending request to flush ! in file:///path/to/angular-mock.js (line 1453)
I added a console.log() in the callback from Service.get() and indeed, it is not called (everything outside of the callback is of course called). Also tried to add a scope digest if not phased after controller creation in the unit test, as I saw suggested in an other question, with no luck.
I know that I can mock that in some other ways, but using $httpBackend seems the perfect solution for the test : mocking the webserver and the data received.
I'm using AngularJS 1.2.16 (can't upgrade to 1.3.*, IE 8 compatibility required). I first used 1.2.13 and updated to check if it would solve the issue, without any luck.
That was an injection issue that was solved by changing the test from
it('should have done stuff irrelevant to the question', function() {
var $injector = angular.injector('so'),
$scope = $injector.get('$rootScope'),
$httpBackend
.whenPOST('/url')
.respond ([]);
// controller needs to be defined here and not in the beforeEach as there
// are more parameters passed to it, depending on the test
var controller = $injector.get('$controller')('controller', { "$scope": $scope });
$httpBackend.flush();
// then here the actual test resolution, also irrelevant
});
To:
it('should have done stuff irrelevant to the question', inject(function(Service) {
// edited lines because they did not change
var controller = $injector.get('$controller')('controller', { "$scope": $scope, "Service": Service });
// edited lines because they did not change
}));
So basicaly, adding the inject() in the test function and passing the service to the controller "manually".
I found the issue, that's great, but I don't really understand why it doesn't work. Also, I tried this right after finding the solution :
it('should have done stuff irrelevant to the question', inject(function() {
// edited lines because they did not change
var Service = $injector.get('Service'),
var controller = $injector.get('$controller')('controller', { "$scope": $scope, "Service": Service });
// edited lines because they did not change
}));
but this fail again, with the same "no pending request" error. I'm guessing that's some sort of racing issue, where my service can't get the proper $httpBackend to be injected when it's created afterwards, but I don't really understand why this occurs. If anybody can enlighten me... I'll be grateful.

Resources