Jasmine $HTTP requests with mock data - angularjs

Im trying to test $http requests and no matter what I do I cant seem to get it to work. I have a factory that holds the 4 request type GET, POST, PUT, DELETE.
Im trying to test the response from a get request but my scope variable that is assigned the response in the controller is giving an error or undefined = the response from the get request.
The factory code is as follows:
app.factory('requestService', ['$http', function ($http) {
// reusable requests
return {
//reuable get request
getRequest: function (url) {
return $http({
method: 'GET',
dataType: "json",
url: url
});
},
//reuable post request
postRequest: function (url, data) {
return $http({
method: 'POST',
data: data,
url: url
});
},
//reuable put request
putRequest: function (url, data) {
return $http({
method: 'PUT',
data: data,
url: url
});
},
//reuable delete request
deleteRequest: function (url) {
return $http({
method: 'DELETE',
url: url
});
}
}
}]);
The following is in the controller.
//get the teams
$scope.getTeams = function(){
requestService.getRequest('https:.../teams').then(function (response) {
$scope.teams = response;
});
}
$scope.getTeams();
Jasmine code:
describe('Controller Test', function() {
var $rootScope, controller;
//Tell the $httpBackend to respond with our mock object
var teamsResponse = {// Teams Data //};
beforeEach(function() {
module('myApp')
});
beforeEach(inject(function($rootScope, $controller, _$httpBackend_, $http) {
$scope = $rootScope.$new();
controller = $controller;
controller('Controller', {$scope: $scope});
$httpBackend = _$httpBackend_;
$httpBackend.whenGET('https:...../teams').respond(200, teamsResponse);
}));
it('should load a list of teams', function() {
$scope.getTeams();
$httpBackend.flush();
expect($scope.teams).toBe(teamsResponse);
});
});
The error I get is:
Expected undefined to be {// Teams Data //}

describe("Testing Common Get JSON Service", function () {
var service, $httpBackend;
beforeEach(inject(function($injector,$rootScope) {
service = $injector.get('CommonService');
$httpBackend = $injector.get('$httpBackend');
scope = $rootScope.$new();
successCallback = jasmine.createSpy();
errorCallback = jasmine.createSpy();
$httpBackend.when('GET', getFlightIncidentLookupUrl).respond(getFlightIncidentLookup);
successCallback();
}));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it('test getServiceJSON works fine and calls a GET method', function () {
service.getServiceJSON(getFlightIncidentLookupUrl).then(function(response) {
expect(response.data.incidentLookup.length).toEqual(1);
});
$httpBackend.flush();
});
});
This worked for me . here CommonService is a service in Angular js which calls a get method. I got the response correctly.

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' });

AngularJs UnitTesting $httpBackend Give No Pending Request To Flush

Here is my test code, and always get the error message above.
'use strict';
describe('getmodules', function () {
window.__env.platform = 'jasmine'; // web || mobile || desktop
beforeEach(function () { module('myApp', 'ngMockE2E', 'ngCookies'); });
var service, $httpBackend, defaultAlertFactory, $cookies, $http;
var $controller;
var $rootScope;
beforeEach(inject(function ($injector) {
service = $injector.get('dashboardService');
$httpBackend = $injector.get('$httpBackend');
$rootScope = $injector.get('$rootScope');
$http = $injector.get('$http');
}));
it("should return a list of getmodules", function () {
debugger;
var result1 = null;
$httpBackend.when('GET', '/home/modulesInfo').respond(true, ['david', 'James', 'Sam']);
/* Code Under Test */
$http({
method: 'GET',
url: '/home/modulesInfo'
}).then(function (success,response) {
result1 =response;
}, function (error) {
});
$httpBackend.flush();
expect(result1).toEqual(["david", "James", "Sam"]);
});
});
========================================================
Not sure why? have the $http.get(), and use the flush() to call the fack httpBackend call, should be good. still not work
You need to change your thenable function syntax as like below:
And check out this working Plunker
$http({
method: 'GET',
url: '/home/modulesInfo'
}).then(function(response) {
console.log('data: ' + JSON.stringify(response.data));
result1 = response.data;
}, function(error) {
});

Jasmine Unit Test for http

I am trying to write unit tests cases around an Angular service. What I am basically trying to test is that the http request is made.
Service
app.service("myService", [
"$http", function ($http) {
this.result = "initial value";
this.get = function (param1, param2) {
return $http({
method: "GET",
url: "api/someService/Get/" + param1 + "/" + param2
})
.success(function (data) {
this.parent.result = data;
console.log(data);
console.log(this.parent.result);
return data;
})
.error(function () {
return "Error occurred";
})
;
};
}
]);
Below is what I have tried.
describe("Surcharge Increase Formula", function () {
var controller;
var scope;
var httpBackend;
var myService;
beforeEach(inject(function($controller, $rootScope, $httpBackend, _myService_) {
scope = $rootScope.$new();
httpBackend = $httpBackend;
myService = _myService_;
});
it('waiver service makes API call', function () {
var response = "This is the response";
httpBackend.when('GET', "api/SurchargeWaiver/Get/0/0").respond(response);
waiverService.get(0, 0);
//httpBackend.flush();
expect(waiverService.result).toEqual(response);
});
What I would like to do is make a call similar to expect(...).toHaveBeenCalled() to see if the api call has been made, or any other way to just test the http call.
Any suggestions?

Implementing Promises in angularJS

I'm attempting to implement some http.get() requests in an angular service, returning a promise.
Here is the excerpt from my initial service:
angular.module('dashboard').service('DashboardHTTP', ['$q', '$http', function ($q, $http) {
this.get_info = function () {
var deferred = $q.defer();
$http.get('/dashboard/4/api/info', { cache: true }).success(function (data) {
deferred.resolve(data);
}).error(function () {
deferred.reject('Could Not Complete Request');
});
return deferred.promise;
}
}]);
And here is an excerpt from the portion of my controller where I call the service:
DashboardHTTP.get_info().then(
function (response) {
var resp = response;
$rootScope.dash_info = resp;
},
function (response) {
return 'error';
},
function (response) {
return 'notify';
});
My questions:
I'm struggling with determining how much testing is needed for an interaction like this. I currently have the following test, which is testing at the service level, but I'm wondering if I need to test at the controller level and if so what sort of testing needs to occur?
beforeEach(inject(function (_$httpBackend_, $injector) {
service = $injector.get('DashboardHTTP');
$httpBackend = _$httpBackend_;
}));
afterEach(function () {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
describe('get_info', function () {
it(' should get info from the url /api/info', function () {
var returnData = { data: 'lots of data' };
$httpBackend.expectGET('/dashboard/4/api/info').respond(returnData);
var returnedPromise = service.get_info();
var result;
returnedPromise.then(function (response) {
result = response;
});
$httpBackend.flush();
expect(result).toEqual(returnData);
});
});
My goal is that I want to set $rootScope.dash_info to the response from the HTTP request made by Service.get_info(). Is my implementation in my controller appropriate? If so, how do I test that the correct data is being passed in at the controller level?
This is probably a partial answer, but here's my input:
Your call is asynchronous, therefore your test should be. Use done.
it(' should get info from the url /api/info', function (done) {
var returnData = { data: 'lots of data' };
$httpBackend.expectGET('/dashboard/4/api/info').respond(returnData);
var returnedPromise = service.get_info();
var result;
returnedPromise.then(function (response) {
result = response;
expect(result).toEqual(returnData);
done();
});
$httpBackend.flush();
});
Also, you do know that http.get returns a promise, right? It has also success and error functions, but it is still a promise.

Jasmine test for an ajax request that returns a promise

I am new to Angular testing.
I am trying to test a simple method in a service that gets some data via an ajax call and returns a promise using Jasmine.
So far very unsuccessfully.
This is the method I am testing:
function getDefaultFibonnacci() {
var deferred = $q.defer();
$http({ method: 'GET', url: '/api/fibonnacci/get' })
.success(function (data) {
deferred.resolve(data);
})
.error(function (data, status) {
deferred.reject(status);
});
return deferred.promise;
}
This is my test code: (Please note all other tests pass apart from 'should return 0,1,1,2,3'
describe('datacontext', function () {
var $httpBackend;
var $rootScope;
var datacontext;
var $q;
beforeEach(function () {
module('app');
inject(function (_$httpBackend_, _$rootScope_, _$q_, _datacontext_) {
$httpBackend = _$httpBackend_;
$rootScope = _$rootScope_;
datacontext = _datacontext_;
$q = _$q_;
});
});
it('should have a getDefaultFibonnacci() function', function () {
expect(angular.isFunction(datacontext.getDefaultFibonnacci)).toBe(true);
});
it('should return a promise', function () {
expect(datacontext.getDefaultFibonnacci().then).toBeDefined();
});
it('should return 0,1,1,2,3', function () {
var sequence = '123';
$httpBackend.when('GET', 'app/dashboard/dashboard.html').respond('');
$httpBackend.when('GET', '/api/fibonnacci/get').respond('0,1,1,2,3');
var deferred = $q.defer();
var promise = deferred.promise;
promise.then(function (response) {
sequence = response.success;
});
datacontext.getDefaultFibonnacci().then(function (data) { deferred.resolve(data); });
$rootScope.$digest();
expect(sequence).toEqual('0,1,1,2,3');
});
});
Guys thanks for all your comments. I learnt a lot through this exercise.
This is the code I ended up with for a passing test.
function getDefaultFibonnacci() {
return $http({ method: 'GET', url: '/api/fibonnacci/get' });
}
it('should return 0,1,1,2,3', function () {
var sequence;
$httpBackend.whenGET('app/dashboard/dashboard.html').respond('');
$httpBackend.expectGET('/api/fibonnacci/get').respond('0,1,1,2,3');
datacontext.getDefaultFibonnacci().then(function (data) {
sequence = data.data;
});
$httpBackend.flush();
expect(sequence).toEqual('0,1,1,2,3');
});
$httpBackend has a flush() method for exactly this reason.
flush() simulates the http server responding, so it will trigger the resolution of your $http.get(). Until you call flush(), nothing will happen (the server hasn't responded yet).
As such, replace your $rootScope.digest() code with $httpBackend.flush() and work from there.
Furthermore, you can save a lot of effort by understanding that $http methods themselves return promises.
This:
function getDefaultFibonnacci() {
var deferred = $q.defer();
$http({ method: 'GET', url: '/api/fibonnacci/get' })
.success(function (data) {
deferred.resolve(data);
})
.error(function (data, status) {
deferred.reject(status);
});
return deferred.promise;
}
Can be simplified to this:
function getDefaultFibonnacci() {
return $http({ method: 'GET', url: '/api/fibonnacci/get' })
}
And will do the same thing.
Finally you don't need another promise in your test. This is enough (remove all reference to $q and deferred and put this directly after your $httpBackend.when(... code):
datacontext.getDefaultFibonnacci()
.then(function (data) {
sequence = data;
});
Are you expecting an object {success: "0,1,1,2,3"} as the response from the http service? You're using response.success when promise resolved
promise.then(function (response) {
sequence = response.success;
});
whereas you're returning a string '0,1,1,2,3'
$httpBackend.when('GET', '/api/fibonnacci/get').respond('0,1,1,2,3');
Also, from the code I see that you don't need to create another promise to test your method.
Try this:
it('should return 0,1,1,2,3', function () {
var sequence = '123';
$httpBackend.when('GET', 'app/dashboard/dashboard.html').respond('');
$httpBackend.when('GET', '/api/fibonnacci/get').respond('0,1,1,2,3');
datacontext.getDefaultFibonnacci().then(function (data) { sequence = data; });
$rootScope.$digest();
expect(sequence).toEqual('0,1,1,2,3');
});
The most important think you forgot to do is call $httpBackend.flush() at some point after making the requests before using the data.
Also you don't need to create an extra promise.
it('should return 0,1,1,2,3', function () {
var sequence;
// Use the shorthand method whenGET
$httpBackend.whenGET('app/dashboard/dashboard.html').respond('');
// We should probably test that this request is actually made, so use expect<method>
$httpBackend.expectGET('/api/fibonnacci/get').respond('0,1,1,2,3');
// Leave out the extra promise code.
// var deferred = $q.defer();
// var promise = deferred.promise;
// promise.then(function (response) {
// sequence = response.success;
// });
// datacontext.getDefaultFibonnacci().then(function (data) { deferred.resolve(data); });
datacontext.getDefaultFibonnacci().then(function (response) {
sequence = response.success;
});
$httpBackend.flush(); // Flush the backend. Important!
// $rootScope.$digest(); // I don't think this is necessary.
expect(sequence).toEqual('0,1,1,2,3');
});
The html template won't be called if you set up your app

Resources