Unittest case using Jasmine for http post method - javascript

I have tried to write a unit test case for post method in angular service. I got $http is undefined error. below is my code. any one tell me what i am missing.
i am adding module using separate file.
service code
sample.factory('AddProductTypeService', function () {
return {
exciteText: function (msg) {
return msg + '!!!'
},
saveProductType: function (productType) {
var result = $http({
url: "/Home/AddProductTypes",
method: "POST",
data: { productType: productType }
}).then(function (res) {
return res;
});
return result;
}
};
});
Jasmine
describe("AddProductTypeService UnitTests", function () {
var $rootScope, $scope, $factory, $httpBackend, basicService,createController, authRequestHandler;
beforeEach(function () {
module('sampleApp');
inject(function ($injector) {
basicService = $injector.get('AddProductTypeService');
// Set up the mock http service responses
$httpBackend = $injector.get('$httpBackend');
});
});
// check to see if it does what it's supposed to do.
it('should make text exciting', function () {
var result = basicService.exciteText('bar');
expect(result).toEqual('bar!!!');
});
it('should invoke service with right paramaeters', function () {
$httpBackend.expectPOST('Home/AddProductTypes', {
"productType": "testUser"
}).respond({});
basicService.saveProductType('productType');
$httpBackend.flush();
});
});
error :
ReferenceError: $http is not defined
Thanks in advance

You have to inject the $http service into your service
sample.factory('AddProductTypeService', ['$http' ,function ($http) {
/* ... */
}]);
https://docs.angularjs.org/guide/di

Related

Unable to cancel request with $resource

After trying some solutions like this: Aborting ngResource using a promise object I'm unable to cancel a request made with $resource.
My last try was with this:
Controller:
angular.module('theApp')
.controller('homeController', function ($q, foodTypeFactory) {
var vm = this;
vm.testButton = function () {
vm.aborter = $q.defer();
foodTypeFactory(vm.aborter).getTest({}, function (data) {
console.log(data);
});
};
vm.cancelButton = function () {
vm.aborter.resolve();
}
});
foodTypeFactory:
angular.module('theApp')
.factory('foodTypeFactory', function ($resource, BACKEND_API) {
return function (aborter) {
return $resource(BACKEND_API + '/api/foodtypes/:id', {id: '#id'}, {
getTest: {
timeout: aborter.promise
}
});
}
});
Once the request is made it completes even if I try to cancel it.
I'm using Angular 1.6.2 with angular-resource 1.6.2.
What am I doing wrong?
What i Can suggest to you is to use an http interceptor .. the you can stop a request... somthing like this:
1) create a file like (auth.interceptor.js:
"use strict";
angular
.module("demo")
.factory('authInterceptorService', ['$q', '$location', 'localStorageService',
function ($q, $location, localStorageService) {
// Public Method
return {
request: function (config) {
config.headers = config.headers || {};
if(!MYCONDITION){ //<-- you can here your logic to test if conitnue request flow or not
return; //<-- TERMINATE IT ..
}else{
return config; //<-- CONTINUE WITH NORMAL REQUEST
}
}
};
}]);
2) in your app.config.js file:
$httpProvider.interceptors.push("authInterceptorService");
Then in ALL your request (via $http or via $resource) this logic is apply ... here you can also put the injection of the Bearer Token if you need it
Hope it help you
Finally I found a solution!
From angular 1.5 $resource can be cancelled with $cancelRequest().
In my case:
Controller:
angular.module('theApp')
.controller('homeController', function (foodTypeFactory) {
var vm = this;
vm.testButton = function () {
vm.onGoingRequest = foodTypeFactory.getTest({}, function (data) {
console.log(data);
});
};
vm.cancelButton = function () {
vm.onGoingRequest.$cancelRequest();
}
});
foodTypeFactory:
angular.module('theApp')
.factory('foodTypeFactory', function ($resource, BACKEND_API) {
return $resource(BACKEND_API + '/api/foodtypes/:id', {id: '#id'}, {
getTest: {
cancellable: true
}
});
});

Testing angular $http inside promise chain with mocha

I have jsdom/mocha/chai set up for backend angular testing.
I have a service that essentially does this (intentionally no post data):
app.service('testService', ['config', '$http', function(config, $http) {
function getSpecificConfig(type) {
return config.getConfig()
.then(function(config) {
// config is coming back defined;
// $http timesout
return $http({method: 'post', url: 'http://localhost:2222/some/path', withCredentials: true});
})
.then(function(res) {
return res.data.config[type];
})
.catch(function(err) {
//handles err
});
};
return {
getConfig: getConfig
}
}]);
my test is:
/* jshint node: true */
/* jshint esversion: 6 */
let helpers = require(bootstrapTest),
inject = helpers.inject,
config,
specificConfig,
mockResponse,
$httpBackend,
$rootScope;
//config service
require('config.js');
//testService I'm testing
require('testService');
beforeEach(inject(function($injector, _$httpBackend_) {
config = $injector.get('config');
specificConfig = $injector.get('testService');
$rootScope = $injector.get('$rootScope');
$httpBackend = _$httpBackend_;
$httpBackend.when('POST', 'http://localhost:2222/some/path')
.response(function(data) {
//would like this to fire
console.log('something happened');
mockResponse = {data: 'some data'};
return mockResponse;
});
}));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectations();
$httpBackend.verifyNoOutstandingRequest();
});
describe ('this service', function() {
beforeEach(function() {
$httpBackend.expect('POST', 'http://localhost:2222/some/path');
$rootScope.$apply(function() {
return specificConfig('something');
});
});
it ('returns the specific config', function() {
expect(mockResponse).to.equal('some data');
})
});
Problem:
When the test is run, the config.getConfig() is resolving properly but the $http leads to a mocha timeout (2000ms) and the afterEach hook throws an Unsatisfied request.
My understanding of this may be completely incorrect so please feel free to educate me on the correct approach (here was my approach):
1) require all necessary dependencies.
2)inject them and set up a $httpBackend listener which fires the test response when the real http is fired.
3) $rootScope.$apply() any promises as the resolution of them is tied to the angular lifecycle.
4) the first before each sets the listener, the second before each fires the service which fires the $http allowing $httpBackend to fire and set the mockResponse.
5) test mock response.
If you need to return promises in your mocked HTTP requests you can use angular-mocks-async like so:
var app = ng.module( 'mockApp', [
'ngMockE2E',
'ngMockE2EAsync'
]);
app.run( [ '$httpBackend', '$q', function( $httpBackend, $q ) {
$httpBackend.whenAsync(
'GET',
new RegExp( 'http://api.example.com/user/.+$' )
).respond( function( method, url, data, config ) {
var re = /.*\/user\/(\w+)/;
var userId = parseInt(url.replace(re, '$1'), 10);
var response = $q.defer();
setTimeout( function() {
var data = {
userId: userId
};
response.resolve( [ 200, "mock response", data ] );
}, 1000 );
return response.promise;
});
}]);

TypeError: parsed is undefined on angularjs service unit test

I am trying to make a unit test for a service that uses $http. I am using Jasmine and I keep on getting this error:
TypeError: parsed is undefined in angular.js (line 13737)
This is what my service looks like:
angular.module('myapp.services', [])
.factory('inviteService', ['$rootScope', '$http', function($rootScope, $http) {
var inviteService = {
token: '',
getInvite: function(callback, errorCallback) {
$http.get('/invites/' + this.token + '/get-invite')
.success(function(data) {
callback(data);
})
.error(function(data, status, headers, config) {
errorCallback(status);
});
}
};
return inviteService;
}]);
This is what my test looks like:
describe ('Invite Service', function () {
var $httpBackend, inviteService, authRequestHandler;
var token = '1123581321';
beforeEach(module('myapp.services'));
beforeEach(inject(function ($injector) {
$httpBackend = $injector.get('$httpBackend');
authRequestHandler = $httpBackend.when('/invites/' + token + '/get-invite').respond({userId: 'userX'}, {'A-Token': 'xxx'});
inviteService = $injector.get('inviteService');
}));
afterEach (function () {
$httpBackend.verifyNoOutstandingExpectation ();
$httpBackend.verifyNoOutstandingRequest ();
});
describe ('getInvite', function () {
beforeEach(function () {
inviteService.token = token;
});
it ('should return the invite', function () {
$httpBackend.expectGET('/invites/' + token + '/get-invite');
inviteService.getInvite();
$httpBackend.flush();
});
});
});
I am pretty new to unit testing angularjs based apps and I used the example in the angularjs documentation
https://docs.angularjs.org/api/ngMock/service/$httpBackend
I am not sure what I could be missing, and I already tried different things and I always get the same error, any help will be appreciated.
The parsed variable is the URL from the service in question. It is undefined for one of the following reasons:
URL is malformed
$http.get is not called
token is not defined
sucess and error callbacks have no data
.respond is not called
.respond does not include a response object as an argument
For example:
describe('simple test', test);
function test()
{
it('should call inviteService and pass mock data', foo);
function foo()
{
module('myapp.services');
inject(myServiceTest);
function myServiceTest(inviteService, $httpBackend)
{
$httpBackend.expect('GET', /.*/).respond(200, 'bar');
function callback(){};
inviteService.getInvite.token = '1123581321';
inviteService.getInvite(callback, callback);
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
}
}
}
References
AngularJS source: angular-mocksSpec.js - "should throw exception when only parsed body differs from expected body object"
AngularJS source: urlUtils.js
AngularJS source: urlUtilsSpec.js

Angular controller promises and testing

Im writing some unit tests for my controller which uses promises.
Basically this:
UserService.getUser($routeParams.contactId).then(function (data) {
$scope.$apply(function () {
$scope.contacts = data;
});
});
I have mocked my UserService. This is my unit test:
beforeEach(inject(function ($rootScope, $controller, $q, $routeParams) {
$routeParams.contactId = contactId;
window.localStorage.clear();
UserService = {
getUser: function () {
def = $q.defer();
return def.promise;
}
};
spyOn(UserService, 'getUser').andCallThrough();
scope = $rootScope.$new();
ctrl = $controller('ContactDetailController', {
$scope: scope,
UserService:UserService
});
}));
it('should return 1 contact', function () {
expect(scope.contacts).not.toBeDefined();
def.resolve(contact);
scope.$apply();
expect(scope.contacts.surname).toEqual('NAME');
expect(scope.contacts.email).toEqual('EMAIL');
});
This give me the following error:
Error: [$rootScope:inprog] $digest already in progress
Now removing the $scope.$apply in the controller causes the test to pass, like this:
UserService.getUser($routeParams.contactId).then(function (data) {
$scope.contacts = data;
});
However this breaks functionality of my controller... So what should I do here?
Thanks for the replies, the $apply is not happening in the UserService. It's in the controller. Like this:
EDIT:
The $apply is happening in the controller like this.
appController.controller('ContactDetailController', function ($scope, $routeParams, UserService) {
UserService.getUser($routeParams.contactId).then(function (data) {
$scope.$apply(function () {
$scope.contacts = data;
});
});
Real UserService:
function getUser(user) {
if (user === undefined) {
user = getUserId();
}
var deferred = Q.defer();
$http({
method: 'GET',
url: BASE_URL + '/users/' + user
}).success(function (user) {
deferred.resolve(user);
});
return deferred.promise;
}
There are a couple of issues in your UserService.
You're using Q, rather than $q. Hard to know exactly what effect this has, other than it's not typical when using Angular and might have affects with regards to exactly when then callbacks run.
You're actually creating a promise in getUser when you don't really need to (can be seen as an anti-pattern). The success function of the promise returned from $http promise I think is often more trouble than it's worth. In my experience, usually better to just use the standard then function, as then you can return a post-processed value for it and use standard promise chaining:
function getUser(user) {
if (user === undefined) {
user = getUserId();
}
return $http({
method: 'GET',
url: BASE_URL + '/users/' + user
}).then(function(response) {
return response.data;
});
}
Once the above is changed, the controller code can be changed to
UserService.getUser($routeParams.contactId).then(function (data) {
$scope.contacts = data;
});
Then in the test, after resolving the promise call $apply.
def.resolve(contact);
scope.$apply();

How to test Angular Factory Objects using Jasmine

The problem I'm trying to solve is the ability to test my factory using Jasmine.
Below is a copy of my app and factory:
var app = angular.module('app', []);
app.factory('service', function ($http) {
return {
getCustomers: function (callback) {
$http.get('/Home/Customers').success(callback);
},
getProfile: function (callback, viewModel) {
$http.post('/Home/Profiles', JSON.stringify(viewModel), {
headers: {
'Content-Type': 'application/json'
}
}).success(callback);
}
};
});
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
I have also setup jasmine but I'm having trouble testing the above "getCustomers" and "getProfile".
Below is my current attempt:
describe("getCustomers", function (service) {
beforeEach(module('service'));
describe('getCustomers', function () {
it("should return a list of customers", inject(function(getCustomers){
expect(getCustomers.results).toEqual(["david", "James", "Sam"]);
}))
})
});
This would be really helpful if someone could provide an example of how to test both "getCustomers" and "getProfile" in two separete tests.
Kind regards.
You can mock the Http GET request and test the service like this
describe("getCustomers", function (service) {
beforeEach(module('app'));
var service, httpBackend;
beforeEach(function () {
angular.mock.inject(function ($injector) {
httpBackend = $injector.get('$httpBackend');
service = $injector.get('service');
})
});
describe('getCustomers', function () {
it("should return a list of customers", inject(function () {
httpBackend.expectGET('/Home/Customers').respond(['david', 'James', 'Sam']);
service.getCustomers(function (result) {
expect(result).toEqual(["david", "James", "Sam"]);
});
httpBackend.flush();
}))
})
});
Working Demo

Categories