Testing AngularJS Service that uses $routeParams as a URL parameter - javascript

I am using Jasmine to test my services. One of my services uses $routeParams as a URL parameter. Now when I test, $routeParams becomes undefined
this is my service code
this.getProjectFunction = function (options) {
$http.get(rootUrl + $routeParams.projectName)
.success(options.success)
.error(options.error);
};
And this is how my test looks like
describe('App Service', function() {
describe('App Service Tests', function(){
var httpBackend, service, optionsSpy, routeParams;
var returnData = [{"id":1,"name":"test"];
beforeEach( module( 'appName' ) );
beforeEach(
inject(
function($httpBackend,projectService,routeParams) {
service = projectService;
optionsSpy = jasmine.createSpyObj('optionsSpy',['success','error','data']);
routeParams = $routeParams;
httpBackend = $httpBackend;
}
)
);
afterEach(function() {
httpBackend.verifyNoOutstandingExpectation();
httpBackend.verifyNoOutstandingRequest();
});
//this should get a specific project
it('should call the getAllProjectsFunction function that will return an argument array',
function(){
//set up some data for the http call to return and test later.
httpBackend.whenGET('../'+routeParams.projectName).respond(returnData);
service.getProjectFunction(optionsSpy);
httpBackend.flush();
expect(optionsSpy.success.mostRecentCall.args[0]).toBe(returnData);
}
);
});
});
Im new to Jasmine testing. Thanks for the help have a good day :)

You need to inject $routeParams not routeParams, and then you can set routeParams.projectName = 'foo'; then set up httpBackend.whenGET('../foo').respond(returnData); (../foo may need to be more like /foo since I don't think the "up one folder" command works here.)

Related

Unable to unit test http call happening through the controller

I am trying to write a code to test the service call which is done in my controller . I am trying to unit test that particular function in controller which is doing the service call and bringing the data . Currently i am trying with local json , but it will actually do a service call .
I got to know that first i have to create a spy object but i am getting the error, my goal is to successfully unit test the http call happening in the controller.
i am new to unit testing .Pasting my code , request you to help me please , struggling in this from many days now.Also i have gone through many solutions , they are so different making be confused.Your help is greatly appreciated
Service code :
//xlpApp is the module name
xlpApp.factory('xlpServices', ['$rootScope', '$http', function($rootScope,
$http) {
var xlpServices = {};
xlpServices.getProgramData = function(){
return $http.get(scripts/services/data/unitTesting.json');
};
unitTesting.json code :
{
"name":"unit testing"
}
Controller Code :
$scope.events = {
programData: function(){
xlpServices.getProgramData().then(function(response) {
if (response && response.data) {
$scope.testVariable= response.data.name;
}
});
},
selectSortingType : function(type) {
$scope.selectedSorting = type;
selectedFilter.sortingBy = $scope.selectedSorting;
}
}
$scope.events.programData();
Unit Test Code :
describe('myProgramGpmCtrl', function() {
beforeEach(module('xlp'));
var $controller;
beforeEach(inject(function(_$controller_){
$controller = _$controller_;
}));
describe('service call test', function() {
var xlpServices , myService , $q;
var $scope = {};
beforeEach(inject(function(xlpServices,_$q_){
xlpServices = xlpServices;
$q = _$q_;
var controller = $controller('myProgramGpmCtrl', { $scope: $scope });
myService = jasmine.createSpyObj('xlpServices',['getProgramData']);
}));
it('Service call test ', function() {
expect(myService.getProgramData).toHaveBeenCalled();
});
});
});
ERROR :
Expected spy xlpServices.getProgramData to have been called.
Try something like,
describe('service call test', function() {
var xlpServicesMock , myService , $q;
var $scope = {};
beforeEach(inject(function(xlpServices,_$q_){
xlpServicesMock = xlpServices;
$q = _$q_;
spyOn(xlpServicesMock ,'getProgramData').and.callFake(function() {
// we can return promise instead this custom object
return {
then: (callback, errorCallback) => {
callback('data to be passed to then callback');
/* `callback` function should be invoked when you wanted to test the service for success response with status code 200.
`errorCallback` function should be invoked with 500 status code when you wanted to test the api failure
Ex: callback({status: 200, data: <your data here>);
errorCallback({status: 500, data: <your error data>})
You can prepare the response to be passed as you wanted.
*/
}
};
});
var controller = $controller('myProgramGpmCtrl', { $scope: $scope, xlpServices: xlpServicesMock });
}));
it('Service call test ', function() {
$scope.events.programData();
expect(myService.getProgramData).toHaveBeenCalled();
});
});
There are good resources available online.
check here and here

Provider $get method is not called in jasmine unit test

I am using custom provider in my app.js to make backend call and then in controllers just injecting that provider and getting promise result (I make this because instead to call getLoggedUser in every controller I am just getting results from provider) and instead of making 10 calls for example I will make just one in provider). But I am unable to test this provider $get method and test is failing because backend call is never made.
I am getting error :
Error: Unexpected request: GET https://localhost:8443/user/current
app.js
angular.module('app', [
...
])
.config(config)
.run(run)
.controller('MainCtrl', MainCtrl)
.value('version', '1.1.0')
.provider('userProvider', function(){
this.$get = ['AuthenticationService',
function(AuthenticationService) {
return AuthenticationService.getLoggedUser();
}];
});
TEST
/* jshint undef:false*/
(function() {
'use strict';
describe('ListingController', function() {
var listingController, rootScope, scope, q, mockAuthenticationService, $httpBackend, service;
var provider = {};
var mockCurrentUser = {
id: 1782,
name: "One, Coordinator",
roleId: [
3, 10
],
eauthId: "coodinator1"
};
beforeEach(module('app'));
beforeEach(module('listing'));
beforeEach(function () {
module(function (userProviderProvider) {
provider = userProviderProvider;
});
});
beforeEach(inject(function($rootScope, $controller, $q, _$httpBackend_, _AuthenticationService_, userProvider, $injector) {
rootScope = $rootScope;
scope = rootScope.$new();
$httpBackend = _$httpBackend_;
q = $q;
mockAuthenticationService = _AuthenticationService_;
// provider = userProvider;
service = $injector.invoke(provider.$get);
rootScope.$digest();
listingController = $controller('ListingController', {
$scope : scope,
mockAuthenticationService : _AuthenticationService_
});
}));
describe('getLogged user and init',function(){
it("should call getLogged function", function() {
listingController.getLoggedUser();
rootScope.$digest();
expect(service).toHaveBeenCalled();
});
});
});
})();
Controller
function getLoggedUser() {
userProvider.then(function (data){
// Here I am getting result from back end call from provider which is good
});
If I make something like this in provider:
this.$get = function (AuthenticationService) {
return {
loggedUser : function() {
return AuthenticationService.getLoggedUser();
}
}
}
I can then make something like this in test:
spyOn(provider , 'loggedUser').and.callFake(function() {
var deferred = q.defer();
deferred.resolve(mockCurrentUser);
return deferred.promise;
});
and this will work test will pass, but with this approach in every controlle when I user userProvider.loggedUser().then it will make additional back end call and with above one only once back end call will be made.
Update for Ceylan
If I do like you suggest to call service and in method call getLoggedUser from another service additional calls are being made every time...not just one like I have without function.
.provider('userProvider', function(){
return {
$get: function(AuthenticationService) {
return new userService(AuthenticationService);
}
}
});
service
function userService(AuthenticationService) {
this.getLoggedUser = function() {
return AuthenticationService.getLoggedUser();
}
}
Here is the basic structure:
$httpBackend.when('GET', 'localhost:8443/user/current').respond(200, /*optional response callback function*/);
$httpBackend.expect('GET', 'localhost:8443/user/current');
//here goes the function that makes the actual http request
$httpBackend.flush();
Be sure that you define your httpBackend var - var httpBackend = $httpBackend;.
And also in order to check did the service was called, you must use spy.
spyOn(service, 'method').and.callThrough();
//here goes the function that calls the service
expect(service.method).toHaveBeenCalled();
You combine the two blocks above and you should be able to achieve what you want.
describe('something',function(){
it("should do something", function() {
spyOn(service, 'method').and.callThrough();
$httpBackend.when('GET', 'localhost:8443/user/current').respond(200,/*optional response callback function*/);
$httpBackend.expect('GET', 'localhost:8443/user/current');
//call the function that makes http request and calls your service
rootScope.$digest();
$httpBackend.flush();
expect(service.method).toHaveBeenCalled();
});
});
And about your service:
function myService(){
var svc = this;
svc.myMethod = function(){
return someDataOrPromise;
}
}

angularJS : how to mock localStorgaeService with spyOn?

I have a simple service implemented like this
sameRoof
.factory('dbService', function (localStorageService, backendUpdate) {
return {
checkProfileAndFlat: function () {
return (localStorageService.get('profile') && localStorageService.get('flatshare'));
}
};
});
LocalStorage are ngModules installed with bower.
I am writint unit test
'use strict';
describe('Service: service taking care of asking the local database', function () {
var localStorageService;
var fakeDB = {'profile' : 'testProfile', 'flatshare' : 'flatshare'};
// load the service's module
beforeEach(module('frontApp'));
// instantiate service
var dbService;
beforeEach(inject(function (_dbService_, _localStorageService_) {
dbService = _dbService_;
localStorageService = _localStorageService_;
//mock localStorageService get/add
spyOn(localStorageService,'get').andCallFake(function(key){
return fakeDB[key];
});
}));
it('should check profile and flatshare', function () {
console.log(localStorageService.get('profile'));
expect( dbService.checkProfileAndFlat() ).toBe(false);
});
});
but i am having problems here,
TypeError: 'undefined' is not a function (evaluating 'spyOn ...)
seems like i am implementing in wrong way the spyOn
the answer is
//mock localStorageService get/add
spyOn(localStorageService,'get').and.callFake(function(key){
return fakeDB[key];
});
as i am using jasmine 2.3.4 and jasmine API has changed compared to the one 1.3

Test AngularJS service initialization

I have an AngularJS service that loads data from localStorage while "initializing" (i.e. in the factory function), like this:
module.service('myService', function ($localStorage) {
var data = $localStorage.data;
if (!isValid(data)) // isValid omitted on purpose, not relevant.
data = undefined;
return {
getData: function() {
return data;
}
setData: function(value) {
if (isValid(value))
data = value;
}
};
}
In my tests, I'd like to check that data is actually loaded from localStorage if the value is present there and valid; this is not about testing isValid, but the service initialization that uses it and $localStorage.
I'd like to be able to call the myService factory inside my test. I'm getting an initialized instance of it in the beforeEach hook since I need to test methods of myService as well. I think I need to have a different instance created for my specific initialization test, but since services are singletons in AngularJS, I'm not sure whether this can be done.
describe('myService', function() {
myService = $localStorage = null;
beforeEach(module('app'));
beforeEach(inject(function($injector) {
myService = $injector.get('myService');
$localStorage = $injector.get('$localStorage');
});
it('should look for stuff in localStorage on creation', function () {
$localStorage.data = 'my data';
// I'd like to call service factory here!!
myService.getData().should.equal('my data');
});
});
Can this be achieved? Does my code have a difficult-to-test structure, or am I disrespecting the "Angular way" and this should be done differently?
Try this:
describe('myService', function() {
myService = $localStorage = null;
beforeEach(module('app'));
beforeEach(inject(function($injector) {
$localStorage = $injector.get('$localStorage');
$localStorage.data = 'my data';
myService = $injector.get('myService');
});
it('should look for stuff in localStorage on creation', function () {
myService.getData().should.equal('my data');
});
});

How do I mock $location.host() in angularjs tests?

I have created an Env service which wraps up environment information, and I'm currently using $location.host() to determine what environment I'm in. How do I mock that in my tests?
I've read https://groups.google.com/forum/?fromgroups#!topic/angular/F0jFWC4G9hI, but it doesn't seem to work, for example:
describe("Env (environment) service", function() {
var Env;
beforeEach(module('App'));
beforeEach(inject(
['Env', function(e) {
Env = e;
}]
));
describe("for staging", function() {
beforeEach(inject(function($location, $rootScope) {
$location.host("http://staging-site.com");
$rootScope.$apply();
}));
it("returns envrionment as staging", function() {
expect(Env.environment).toEqual("staging");
});
it("returns .isStaging() as true", function() {
expect(Env.isStaging()).toBeTruthy();
});
});
});
I've also tried the $browser variant, but that doesn't work either. Any ideas?
The best way is to use spies IMHO: http://tobyho.com/2011/12/15/jasmine-spy-cheatsheet/
// inject $location
spyOn($location, "host").andReturn("super.domain.com");
var host = $location.host();
alert(host) // is "super.domain.com"
expect($location.host).toHaveBeenCalled();
Syntax for jasmine 2.0 and greater has changed as follows
// inject $location
spyOn($location, "host").and.returnValue("super.domain.com");
var host = $location.host();
alert(host) // is "super.domain.com"
expect($location.host).toHaveBeenCalled();
I had similar problem and have used $injector service. (I don't know if it is the simplest solution, but it worked for me :) )
Since $location cannot be relied in during tests I have prepared my own mock.
First, you need to create a factory method. (Or service or provider if you prefer - see https://gist.github.com/Mithrandir0x/3639232 for comparison):
function locationFactory(_host) {
return function () {
return {
/* If you need more then $location.host(), add more methods */
host: function () {
return _host;
}
};
};
}
Then before you create your 'Env', feed injector with this $location mock:
module(function ($provide) {
$provide.factory('$location', locationFactory('http://staging-site.com'));
});
Now every time your access $location in your code your mock is injected, so it returns whatever you need it to.
More on $provide method is in angular docs
Hope this helps you in the future.
Update: I see one place when you might have gone wrong (or which at least would be wrong in my solution). It seems like you are initiating you 'Env' module (which I guess calculates the data immediately) and only after this you change $location - which might be already too late.
There is a good example in the doc: https://docs.angularjs.org/guide/services
You want to make sure to use $provide before you instantiate the service. I like to use $injector in the testcase, to first decide the location then instantiate the service.
var mockLocation;
beforeEach(function() {
// control the $location.host() function
// which we fake in the testcases.
mockLocation = {
host: jasmine.createSpy()
};
module(function($provide) {
$provide.value('$location', mockLocation);
});
});
then
describe('staging server:', function() {
beforeEach(function() {
// mockLocation.host.andReturn('http://staging-site.com'); // jasmine 1.x
mockLocation.host.and.returnValue('http://staging-site.com');
});
it('returns envrionment as staging', inject(function($injector) {
Env = $injector.get('Env');
expect(Env.environment).toEqual('staging');
}));
});
and
describe('production server:', function() {
beforeEach(function() {
// mockLocation.host.andReturn('prod.example.com'); // jasmine 1.x
mockLocation.host.and.returnValue('prod.example.com');
});
it('returns envrionment as production', inject(function($injector) {
Env = $injector.get('Env');
expect(Env.environment).toEqual('production');
}));
});
Here's another way to mock $location, with Sinon.js
locationMock = {
path: sinon.spy()
};
And an example assertion:
locationMock.path.should.be.calledWith('/home');

Categories

Resources