var app= angular.module('app', []);
below is my factory method that will get the data from the sample.json
app.factory('factoryGetJSONFile', function($http) {
return {
getMyData: function(done) {
$http.get('mock/sample.json')
.success(function(data) {
done(data);
})
.error(function(error) {
alert('An error occured whilst trying to retrieve your data');
});
}
}
});
below is my controller. I can able to access the service data in my controller
app.controller('homeController', ['$scope', 'factoryGetJSONFile', function ($scope, factoryGetJSONFile) {
factoryGetJSONFile.getMyData(function (data) {
$scope.name = data.projectDetails.name;
$scope.duration = data.projectDetails.duration;
console.log($scope.name+ " and duration is " + $scope.duration);
});
}]);
Below is my sample.json
{
"status": 200,
"projectDetails": {
"name": "Project Name",
"duration": "4 Months",
"business_place": "Dummy address"
}
}
How to write the unit test cases for the above get service. I would like to test projectDetails.name in my test cases.
To mock http responses you can use the $httpbackend service. For example, if you want to test the getMyData method of the object created by your factory, you'd do something like:
var $httpbackend,
factoryGetJSONFile;
var sample.json = // Your sample JSON
var getUrl = 'mock/sample.json';
beforeEach(inject(function(_$httpbackend_, _factoryGetJSONFile_) {
// Load app
module('app');
// Make services available in tests
$httpbackend = _$httpbackend_;
factoryGetJSONFile = _factoryGetJSONFile;
// Mock the server's response
$httpbackend.when('GET', getUrl).
respond(sample.json);
}));
It('factoryGetJSONFile.getMyData should make correct GET request', function() {
// Call your services method and flush $httpbackend so the mock response is sent
var response = factoryGetJSONFile.getMyData();
$httpBackend.flush();
expect(response).toEqual(jasmine.objectContaining(sample.json));
});
Related
I am trying to write the test cass for the factory which is returing a JSON response.
But I am getting the error:
Error: [$injector:unpr] http://errors.angularjs.org/1.4.1/$injector/unpr?p0=serviceProvider%20%3C-%20service
at Error (native)
Here is my code:
(function () {
angular.module('uspDeviceService',[]).factory('getDevice', GetDevice);
GetDevice.$inject = ['$http'];
function GetDevice($http) {
getDeviceList = function() {
return $http.get("static/test-json/devices/device-list.json");
}
return {
getDeviceList: getDeviceList
}
}
}());
Code for Test case:
describe('Get Product test', function() {
beforeEach(module('uspDeviceService'));
var service, httpBackend, getDevice ;
beforeEach(function () {
angular.mock.inject(function ($injector) {
//Injecting $http dependencies
httpBackend = $injector.get('$httpBackend');
service = $injector.get('service');
getDevice = $injector.get('getDevice');
})
});
console.log('Injection Dependencies is done');
describe('get Device List', function () {
it("should return a list of devices", inject(function () {
httpBackend.expectGET("static/test-json/devices/device-list.json").respond("Response found!");
httpBackend.flush();
}))
})
});
I am new to Angular Unit testing, can anyone please help me, where I am going wrong..
Two things that jump out at me:
Your angular.module declaration is defining a module, not getting the module. I would encourage you to split that up so that it's a fair bit more clear what your intent is.
angular.module('uspDeviceService', []);
angular.module('uspDeviceService').factory('getDevice', GetDevice);
It likely works as-is, but clarity is important.
What is...service? It's not defined anywhere in your code, and Angular can't find it either, hence the error message. You may be looking to get getDevice instead. Also, name your test variable with respect to what it actually is, so you don't confuse yourself.
// defined above
var getDevice;
// while injecting
getDevice = $injector.get('getDevice');
Supposing that you have an angularjs controller myController defined in myModule. The controller do some action when the api call is success and shows a flash message when api returns success = false. The your controller code would be something like
angular.module('myModule')
.controller( 'myController', function ( $scope,flashService, Api ) {
Api.get_list().$promise.then(function(data){
if(data.success) {
$scope.data = data.response
}
else{
flashService.createFlash(data.message, "danger");
}
});
});
Now to test both success = true and success = false we
describe('myController', function(){
var $rootScope, $httpBackend, controller, flashService;
var apilink = 'http://apilink';
beforeEach(module('myModule'));
beforeEach(inject(function(_$httpBackend_,_$rootScope_, _$controller_, _flashService_) {
$rootScope = _$rootScope_;
$httpBackend = _$httpBackend_;
flashService = _flashService_;
controller = _$controller_("myController", {$scope: $rootScope});
}));
it('init $scope.data when success = true', function(){
$httpBackend.whenGET(apilink)
.respond(
{
success: true,
response: {}
});
$httpBackend.flush();
expect($rootScope.data).toBeDefined();
});
it('show flash when api request failure', function(){
spyOn(flashService, 'createFlash');
$httpBackend.whenGET(apilink)
.respond(
{
success: false
});
$httpBackend.flush();
expect(flashService.createFlash).toHaveBeenCalled();
});
});
You are always going to mock the response because here we are testing the javascript code behaviour and we are not concerned with the Api. You can see when success the data is initialized and when success is false createFlash is called.
As far as test for factory is concerned you can do
describe('Get Product test', function() {
beforeEach(module('uspDeviceService'));
var service, httpBackend, getDevice ;
beforeEach(function () {
inject(function ($injector) {
httpBackend = $injector.get('$httpBackend');
service = $injector.get('service');
getDevice = $injector.get('getDevice');
});
});
describe('get Device List', function () {
it("should return a list of devices", inject(function () {
httpBackend.expectGET("static/test-json/devices/device- list.json").respond("Response found!");
var result = getDevice.getDeviceList();
httpBackend.flush();
expect(result).toEqual('Response found!');
}));
});
});
Still rather new to angular unit testing. I have a service in module 'example' that loads a local JSON file through the $http service and asynchronously returns the response data.
I figured out that I need to test (using Jasmine) that
the http GET connects with the local resource
the http service loads the JSON and gets the correct json content
the service fulfills its promise to return the response data
my service code
/**
* Service to load JSON data.
*/
.service('jsonLoader', ['$http','$timeout', 'TABLE_DATA_LOC', function($http, $timeout, TABLE_DATA_LOC) {
this.load = function() {
return $timeout(function() {
return $http.get(TABLE_DATA_LOC).then(function(response) {
return response.data;
});
}, 30);
};
what I have for the test currently:
describe('jsonLoader service', function() {
var jsonLoader, httpBackend;
beforeEach(module("example"));
beforeEach(inject(function(_jsonLoader_, $httpBackend) {
jsonLoader = _jsonLoader_;
httpBackend = $httpBackend;
}));
it("should load json", function() {
httpBackend.whenGET('./mock/sample.json').respond({
"people": [
{
"person": {
"firstName": "jim",
"lastName": "bob"
}
}
]
});
});
});
is the first part right, and how would I use jasmine to test the async promise?
Following on from my comment, here's how I would approach it.
describe('jsonLoader service', function() {
var uri;
beforeEach(module('example', function($provide) {
$provide.constant('TABLE_DATA_LOC', uri = 'mock/sample.json');
}));
it('should load JSON in a $timeout and return the response data', inject(function($httpBackend, $timeout, jsonLoader) {
var responseData = 'whatever', resolved = false;
$httpBackend.expectGET(uri).respond(responseData);
jsonLoader.load().then(function(data) {
expect(data).toBe(responseData);
resolved = true;
});
$timeout.flush();
$timeout.verifyNoPendingTasks();
expect(resolved).toBeFalsy();
$httpBackend.flush();
$httpBackend.verifyNoOutstandingRequest();
expect(resolved).toBeTruthy();
}));
});
Plunker demo ~ http://plnkr.co/edit/jmc9FWjbOkpmT6Lu8kVn?p=preview
I'm attempted to unit test a service. I've injected the service however the method call getAllProducts() doesn't appear to run however the test still passes!
Plnkr
service.js
angular.module('vsApp')
.factory('productsDataService', function($http) {
var service = {
getAllProducts: getAllProducts
};
// get all products
function getAllProducts() {
return $http.get('/apiv1/getAllProducts/').then(function(data) {
return (data);
});
}
return service;
});
spec.js
// jasmine
describe('products data service', function () {
var $httpBackend, productsDataService;
beforeEach(module('vsApp'));
beforeEach(inject(function(_$httpBackend_, _productsDataService_) {
$httpBackend = _$httpBackend_;
productsDataService = _productsDataService_;
}));
it('should get all products', inject(function() {
console.info("get all");
// mock response for the http call in the service
$httpBackend.when('GET', '/apiv1/getAllProducts/')
.respond({name: 'item', price: '932'});
//this doesn't seem to run??
productsDataService.getAllProducts().then(function(response) {
expect(response.data.length).toBeGreaterThan(1);
});
}));
});
Ok, you have to make it sync. (all pending request will get resolved) using $http.flush();
Working demo as expected
productsDataService.getAllProducts().then(function(response) {
console.log(response);
expect(response.data.length).toBeGreaterThan(999);
});
$httpBackend.flush(); // <=============== here.
Ok here is the scenario we are trying to unit test with Jasmine. We have a service defined similar to the service below:
(function () {
'use strict';
angular.module('mymodule')
.service('myservice', myservice);
myservice.$inject = ['$q', '$resource', 'progressService',
'myservice2', 'myservice3', 'ngDialog'];
function myservice($q, $resource, progressService,
myservice2, myservice3, ngDialog) {
var self = this;
self.dataListSvc2 = [];
self.dataListSvc3 = [];
self.dataFromResource = null;
self.myRoutine = myRoutine;
var myResource = $resource('/someurl/webapi/GetData');
//TRYING TO TEST THIS ROUTINE!!!
function myRoutine(param1, param2) {
return progressService.show($q.all([
myResource.get({ Param1: param1 }).$promise.then(function (response) {
self.dataFromResource = response;
}),
myRoutine2(param2),
myRoutine3(param2)
]));
}
function myRoutine2(param) {
return myservice2.getSomeData(param).then(function (response) {
var results = [];
self.dataListSvc2 = [];
response.forEach(function (item) {
item.AddField1 = null;
item.AddField2 = false;
results.push(item);
});
self.dataListSvc2 = results;
});
}
function myRoutine3(param) {
return myservice3.getSomeMoreData(param, [6])
.then(function (response) {
self.dataListSvc3 = response;
});
}
}
})();
I am trying to write a unit test for myservice.myRoutine() and not having any luck, I suspect it is due to the $q.all array of promises. Here is a test I settled on but it is not ideal and honestly I don't feel like it's testing anything of value. I have also tried "mocking" the three requests with $httpBackend with no luck as all three responses come back in an array with undefined values. I searched around SO and the web and I am only finding $q.all([]) unit tests referencing controllers but not services. If anyone has some input it would be much appreciated. Here is where I settled for the time being:
describe('My Service: ', function () {
var $httpBackend;
beforeEach(module('mymodule'));
beforeEach(inject(function ($injector) {
// Set up the mock http service responses
$httpBackend = $injector.get('$httpBackend');
}));
it('Can call myroutine from myservice', inject(function (myservice) {
//Arrange
var expectedVal1 = 1234;
var expectedVal2 = 1234;
spyOn(myservice, "myRoutine");
//Act
myservice.myRoutine(expectedVal1, expectedVal2);
//Assert
expect(myservice.myRoutine).toHaveBeenCalled();
}));
});
Use the $httpBackend.when('GET', '/someurl/webapi/GetData').respond(data); to fake the http requests. You should fake the myservice2.getSomeData method too and the myservice3.getSomeMoreData I guess those methods create http request too so you could fake them in the same manner.
expect(myservice.myRoutine).toHaveBeenCalled();
Is irrelevant as you called it manually ;-).
var fixture = {/*some data ... */};
it('Can call myroutine from myservice', inject(function (myservice) {
$httpBackend.when('GET', '/someurl/webapi/GetData').respond(fixture);
//Act
myservice.myRoutine(expectedVal1, expectedVal2);
$rootScope.$apply()
//Assert
expect(myservice.dataFromResource).toBe(fixture);
}));
I have seen some questions regarding this but all of them was specific to each case and I couldn't find a solution for my case in those posts.
I have a current controller:
function Login(authService, $scope) {
var vm = this;
vm.submit = submit;
vm.form = {};
function submit() {
if ($scope.loginForm.$invalid) {
vm.invalid = true;
return;
} else {
var data = {
usr: vm.form.email,
pwd: vm.form.password,
vendorId: 99
};
authService.login(data).then(success, error);
}
}
function success(res) {
if (res.data) {
//Do stuff
}
}
function error(error) {
console.log("Error ", error);
}
}
And the following unit test:
describe('Login', function() {
beforeEach(module('app'));
var loginCtrl, scope, $httpBackend, authService;
var loginResponse = [{
"data": {
"avatar": "avatar",
"gender": "M",
"hid": "hid,
"id": "id",
"role": "Adult",
"token": "token"
}
}];
var loginRequest = { "usr": "test#teste.com", "pwd": "123teste!", "vendorId": 99 };
beforeEach(inject(function($rootScope, _$httpBackend_, $controller, _authService_) {
$httpBackend = _$httpBackend_;
scope = $rootScope.$new();
loginCtrl = $controller('Login', {
$scope: scope
});
authService = _authService_;
}));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
describe("submit", function() {
it("should send login data to the server", function() {
// expect(loginCtrl.login).toBe(false);
//Tells the $httpBackend service to expect a POST call to be made to a service and that it will return
//loginResponse object that was defined before
$httpBackend.expectPOST('api/current/api/login').respond(loginResponse);
//Execution of the service
var deferred = authService.login(loginRequest);
var users;
deferred.then(function(response){
users = response.data;
});
// expect(loginCtrl.login).toBe(true);
//Preserve the asynchronous nature of the call while at the same time be able to test the response of the call
$httpBackend.flush();
// dump(users);
expect(users).toEqual(loginResponse);
// expect(loginCtrl.login).toBe(true);
});
});
});
And I am getting the error:
Error: Unexpected request: GET signup/signup.html
No more request expected
I have found why this error occurs (I think). I'm using ui-router and it seems that it is always trying to do a GET request for the router root:
$urlRouterProvider.otherwise('/signup/');
$stateProvider
/* NOT AUTHENTICATED STATES */
.state('signup', {
url: '/signup/',
templateUrl: 'signup/signup.html',
controller: 'Signup as signupCtrl',
data: {
authorizedRoles: [AUTH_EVENTS.notAuthenticated]
}
})
Now I have no idea why, or how to fix it... Can someone understand what I'm doing wrong?
Thanks in advance!
Edit: authService
function authService($http, Session) {
var service = {
login : login
};
return service;
function login(credentials) {
console.log('authservice:', credentials);
return $http.post('api/current/api/login', credentials).then(function(res){
if (res.data.data){
var user = res.data.data;
Session.create(user.id, user.hid, user.token, credentials.usr, user.role, user.gender);
}
return res.data;
});
}
}
The template is requested, but you didn't inform the $http mock that it would be.
Register your template with $httpBackend
$httpBackend.expect('GET', 'signup/signup.html');
https://docs.angularjs.org/api/ngMock/service/$httpBackend#expect
Updated your function and try this code should work. Another option is to add ur template in template cache using gulp if you are using gulp this template get call problem is very specific with ui router and i have seen bug posted against it in github.
$httpBackend.expectGET('signup/signup.html').respond(200);
$httpBackend.flush();
$httpBackend.expectPOST('api/current/api/login').respond(loginResponse);
//Execution of the service
var deferred = authService.login(loginRequest);
var users;
deferred.then(function(response){
users = response.data;
});
// expect(loginCtrl.login).toBe(true);
//Preserve the asynchronous nature of the call while at the same time be able to test the response of the call
$httpBackend.flush();
// dump(users);
expect(users).toEqual(loginResponse);
// expect(loginCtrl.login).toBe(true);