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
Related
I have found many articles here how to test Angular's config phase and I was able to create my tests against restangular and LocalStorageModule module configuration. The only one I cannot solve yet is checking whether the interceptor was added or not. I do not need to test the service because it is a 3rd party stuff, I consider it is already tested - hopefully.
The question is that, how can I spy on $httpProvider.interceptors.push method which is called in configuration phase?
Thanks for any help in advance!
Here is my code:
(function () {
'use strict';
angular.module('myapp', [
// Angular modules
'ngAnimate',
'ngRoute',
// Custom modules
'myapp.layout',
// 3rd Party Modules
'LocalStorageModule',
'http-auth-interceptor',
'restangular'
])
.config(function (RestangularProvider) {
RestangularProvider.setBaseUrl('http://.../services/webapi/');
})
.config(function (localStorageServiceProvider) {
localStorageServiceProvider.setPrefix('myapp');
})
.config(function($httpProvider) {
$httpProvider.interceptors.push('authInterceptorFactory');
});
})();
'use strict';
describe('myapp configuration', function() {
var RestangularProvider,
localStorageServiceProvider,
$httpProvider;
//modules
beforeEach(function () {
angular.module('myapp.layout', []);
angular.module('http-auth-interceptor', []);
});
//providers
beforeEach(function () {
module('restangular', function(_RestangularProvider_) {
RestangularProvider = _RestangularProvider_;
spyOn(RestangularProvider, 'setBaseUrl').and.callThrough();
});
module('LocalStorageModule', function (_localStorageServiceProvider_) {
localStorageServiceProvider = _localStorageServiceProvider_;
spyOn(localStorageServiceProvider, 'setPrefix').and.callThrough();
});
module('myapp', function(_$httpProvider_) {
$httpProvider = _$httpProvider_;
spyOn($httpProvider.interceptors, 'push').and.callThrough();
});
//module('myapp');
inject();
});
describe('Restangular configuration', function() {
it('setBaseUrl is set up', function() {
expect(RestangularProvider.setBaseUrl).toHaveBeenCalled();
});
});
describe('localStorage configuration', function() {
it('setPrefix is set up', function () {
expect(localStorageServiceProvider.setPrefix).toHaveBeenCalled();
});
});
describe('$httpProvider configuration', function() {
it('an interceptor is added', function() {
expect($httpProvider.interceptors.push).toHaveBeenCalled();
});
});
});
I've just been doing this myself and its actually surprisingly easy. Below are two ways you can do this, the first is the way I would recommend.
The thing to keep in mind is when you initialise a module the config part will be run automatically, you can use this to either test directly or help set up a test.
Option 1 - Using a fake module to setup
describe('config sets $httpProvider interceptor', function () {
var $httpProvider;
beforeEach(function () {
// First we initialise a random module, we will get $httpProvider
// from it and then use that to spyOn.
module(function (_$httpProvider_) {
$httpProvider = _$httpProvider_;
spyOn($httpProvider.interceptors, 'push');
});
// Now we initialise the app we want to test, $httpProvider will be
// the spy from before.
module('myapp');
inject();
});
it('should add to $httpProvider interceptors', function () {
expect($httpProvider.interceptors.push)
.toHaveBeenCalledWith('authInterceptorFactory');
});
});
Option 2 - Using just your module
describe('config sets $httpProvider interceptor', function () {
var $httpProvider;
beforeEach(function () {
// First we initialise a your module, we will get $httpProvider
// from it and then use that to assert on.
module('myapp', function (_$httpProvider_) {
$httpProvider = _$httpProvider_;
});
inject();
});
it('should add to $httpProvider interceptors', function () {
expect($httpProvider.interceptors).toEqual(['authInterceptorFactory']);
});
});
Again, my recommendation (and the way I did it) is with option 1.
I am using an AngularJS service in my app. I've learned that the service I've created is unreliable. For that reason, I want to build some unit tests around it. While the service works fine in my AngularJS app, I'm having problems getting it to work with Jasmine. Its like I can't load any modules. I've strip down the code to the bare bones. I do not understand what I'm doing wrong. My code looks like this:
myService.js
'use strict';
angular.module('customModule', [])
.factory('$serviceName', [function () {
return {
isAvailable: function () {
return true;
}
};
}]
);
myService.spec.js
describe('customModule', function() {
beforeEach(function() {
console.log('loading module...');
module('customModule');
});
describe('$serviceName', function () {
var myService = null;
beforeEach(inject(function ($serviceName) {
console.log('loading service...');
myService = $serviceName;
}));
it('should work', function () {
console.log('testing');
var isAvailable = myService.isAvailable();
console.log(isAvailable);
expect(1 + 2).toEqual(3);
});
});
})
gruntfile.js
'use strict';
module.exports = function (grunt) {
grunt.initConfig({
jasmine: {
unitTests: {
src: 'test/*.js',
}
}
});
require('load-grunt-tasks')(grunt);
grunt.registerTask('default', ['jasmine:unitTests']);
};
My jasmine tests are running. However, its like myService.js isn't being loaded. I'm not sure how to do that. I'm also not sure how to get 'angular' (used in myService.js) in the tests either.
Thank you for your help.
Do you load you Angular application in myService.spec.js? Otherwise $serviceName is not available for dependency injection.
You should add something like this in your test file:
beforeEach(module('angularApp'));
(of course replace angularApp with the name you are using)
And it is also a good idea to use another beforeEach block to handle the dependency injection so you can use it in all tests in myService.spec.js. In total you should have something like this:
describe('serviceName', function () {
beforeEach(module('angularApp'));
var iMyService
// Initialize the service
beforeEach(inject(function ($serviceName) {
iMyService = $serviceName;
}));
it('should check if available', function () {
var isAvailable = iMyService.isAvailable();
console.log(isAvailable);
expect(1 + 2).toEqual(3);
});
});
I am using angular-mock to inject my controller for unit testing. I am failing to do so since I keep getting the following error.
[$injector:unpr] Unknown provider: PatientRecordsControllerProvider <- PatientRecordsController
Here is my code setup -
(function () {
angular.module('patient_profile', ['ngRoute']);
})();
(function () {
var PatientRecordsController = function () {
};
angular.module('patient_profile').controller('PatientRecordsController', PatientRecordsController);
})();
And my test case
describe('PatientRecordsController:unit-testing', function () {
beforeEach(module('patient_profile'));
it('timeline should be an array', inject(['PatientRecordsController',
function (controller) {
//Cant do stuff
}
]));
});
UPDATE The same procedure works perfectly fine with services. How come?
Controller has to be instantiated using $controller service. Isn't the below format of test cleaner?
describe('PatientRecordsController:unit-testing', function () {
var controller;
beforeEach(function(){
module('patient_profile');
inject(function(_$controller_){
controller = _$controller_('PatientRecordsController', {});
});
});
it('timeline should be an array', function(){
//DO STUFF using controller
});
});
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.)
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');