How do I mock $http in AngularJS service Jasmine test? - angularjs

I am trying to test an AngularJS service carService, but the $httpBackend does not seem to work.
//carService
angular.module('services').factory('carService',
function($http) {
return {
getTypes: function() {
return $http.get('/api/cars/types');
}
};
});
Can anybody explain why the response is null?
describe("Services", function () {
beforeEach(module("app.services"));
describe("Car services", function () {
var service, $httpBackend;
beforeEach(inject(function($injector) {
service = $injector.get('carService');
$httpBackend = $injector.get('$httpBackend');
$httpBackend.when('GET', "/api/cars/types").respond(["Toyota", "Honda", "Tesla"]);
}));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it('getTypes - should return 3 car manufacturers', function () {
service.getTypes().then(function(response) {
expect(response.length).toEqual(3); //the response is null
});
$httpBackend.flush();
});
});
});

Try this:
expect(response.data.length).toEqual(3);
The response object returned by a $http request has the response data within the data property (docs).

Related

Spying on factory which uses $http get to use to check function is callled

I have written a factory which uses the $http get method to retrieve the json data. Now I'm trying to add a unit test for the factory to check whether the function is called or not
This is my factory
.factory('dataFactory',function($http){
function getData(){
var request = $http({
method: 'GET',
url: 'http://localhost/data.json'
});
return request;
}
var service = {
getData : getData
};
return service;
});
This is my controller
.controller('jsonController',function($scope,$state,$stateParams,stringService,dataFactory){
dataFactory.getData()
.then(function(response){
if(response){
$scope.result = response.data.record;
console.log(response.data.record);
} else {
// empty data message
}
})
.catch(function(error){
console.log('something went wrong', error);
});
});
This is my Test
describe('Testing factory', function() {
beforeEach(module('factories'));
var mySpy;
mySpy = jasmine.createSpy('service');
beforeEach(module(function ($provide){
$provide.value('service',mySpy)
}));
describe('get json List', function () {
it('should return a list', inject(function () {
spyOn(service, 'getData');
expect(service.getData).toHaveBeenCalled();
}));
});
});
its returning an error:
service is not defined
Answer
Because you're testing an API / GET return, you DON'T want a Spy! It looks like you're unit test is technically what I would call an "API" or "Integration" Test -- you're checking the data return from an endpoint. (There's nothing wrong with that, but that makes the test setup different).
AngularJS has in its ngMocks a built in service for this called $httpBacked.
Code (spec file)
describe('Testing Darshuu', function() {
beforeEach(module('Darshuu'));
// var mySpy;
var service = null;
var $httpBackend;
// Setup Spies in beforeEach() blocks!
// mySpy = jasmine.createSpy('service');
beforeEach(module(function($provide){
// $provide.value('service',mySpy)
// $provide => manually provide some object or
// function IN PLACE OF our factory
// Since we're in a Unit Test for the factory,
// we actually want to inject it and NOT mock it
// completely.
// See next beforeEach()
}));
// _..._ notation for injecting in specs
beforeEach(inject(function(_dataFactory_, _$httpBackend_) {
service = _dataFactory_;
$httpBackend = _$httpBackend_;
}));
/*
No call is ever made to getData() so this spec
*should* fail, technically.
*/
// describe('get json List', function () {
// it('should return a list', inject(function () {
// /*
// No call is ever made to getData() so this spec
// *should* fail, technically.
// */
// spyOn(service, 'getData');
// expect(service.getData).toHaveBeenCalled();
// }));
// });
describe("DataFactory", function() {
beforeEach(function() {
// Since you know to expect an $http request, use the testing
// mock for it: $httpBackend
// THEN, mock the response for the "API".
// =====
$httpBackend.whenGET('http://localhost/data.json').respond(200, {
"test": true,
"data": [{id:1, name: "Picard"}, {id: 2, name: "Riker"}, {id: 3, name: "Data"}]
});
})
it('HAS a getData() method', function() {
expect( service.getData ).toBeDefined();
expect( typeof service.getData ).toEqual("function");
})
// BECAUSE there's no AJAX mocking here OR data, it'll fail.
// I have no idea what your hosting/API is like, but this is
// a start I hope!
it("getData() - returns a JSON list (array)", function() {
service.getData().then(function(response) {
expect( response.data ).toBeDefined();
expect( typeof response.data ).toEqual("object");
});
$httpBackend.flush();
})
})
});
Plnkr
Karma+Jasmine Angular Plnkr
Define factory as object helps you to return methods in your controller as object function, for example:
app.factory('dataFactory',function($http){
var factory = {};
factory.getData = function(){
var request = $http({
method: 'GET',
url: 'http://localhost/data.json'
});
return factory;
});
Controller
app.controller("ctrl", function(dataFactory) {
dataFactory.getData(); //worked
})

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

unit test $http calls inside services

I am trying to test $http calls inside services which, upon $http response, store the response data in service itself (does not return response to controller). Most examples I found (even AngularJs documentation) are testing $http calls in controller. For ex:
app.factory('dataFactory', function($http){
return {
getData: function(){
return $http.get('https://some-url');
}
}
});
app.controller('MainCtrl', function($scope, dataFactory, $http) {
$scope.name = 'World';
dataFactory.getData().success(function(data){
$scope.data = data;
})
});
The unit test for this code is:
describe('with httpBackend', function() {
beforeEach(inject(function($controller, $rootScope, $httpBackend) {
$scope = $rootScope.$new();
$httpBackend.when('GET', 'https://some-url')
.respond({things: 'and stuff'});
MainCtrl = $controller('MainCtrl', { $scope: $scope });
$httpBackend.flush();
}));
it('should set data to "things and stuff"', function() {
expect($scope.data).toEqual({
things: 'and stuff'
});
});
});
But in my services, I am making the calls in following fashion:
app.service('dataService', function($http) {
var self = this;
this.getData = function() {
$http.get('https://some-url/')
.then(
function success(response) {
self.data = response.data
},
function error(msg) {
console.log(msg);
}
);
};
});
For this, I will need to unit test the service and not the controller.
EDIT: Below is the unit-test I've written (which is passing, but not sure it's the correct approach):
describe('.getData()', function() {
beforeEach(inject(function($httpBackend) {
$httpBackend.when('GET', 'https://some-url/')
.respond({data: 'sample data'});
dataService.getData();
$httpBackend.flush();
}));
it('should store data correctly', function() {
expect(dataService.data).toEqual({data: 'sample data'});
});
});
Need some help regarding the unit-testing approach I should follow to test services with $http calls (and store data).

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

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

Resources