I'm using document.hidden and I need to mock it as true or false in my unit tests.
So I made this:
documentMock = jasmine.createSpyObj('document', ['hidden']);
module(function ($provide) {
$provide.value('document', documentMock);
});
But hidden is always returning false.
$provide is used to register things with Angular's injector. Unless your code under test is injecting something called document (which is not good, since that will conflict with the global document object), you should be doing a $provide.value('$document', documentMock) instead. $document is an Angular injectable and a wrapper around the global document object, so the code under test should also be referencing $document.
Related
using angular 1.5 / es6, I have the following code that I want to test :
this.$onDestroy = function () {
this.$interval.cancel(someIntervalTimer);
};
I cant find out how to trigger the destruction of the controller in jasmine unit test in order to test what is happening there.
I tried to $destroy() $scope and $rootScope, also $broadcast('$destroy'), etc but none of these seems to trigger the destruction.
$onDestroy hook is called by directive/component compiler, as well as other controller hooks.
When controller is tested directly with $controller or $componentController, hook method is supposed to be called manually:
spyOn(controllerInstance.$interval, 'cancel');
expect(controllerInstance.$onDestroy).toBe(jasmine.any(Function));
controllerInstance.$onDestroy();
expect(controllerInstance.$interval.cancel).toBeCalledWith(jasmine.any(Object));
beforeEach(inject(function (_$controller_, _$injector_) {
ctrl = _$controller_(...) // 1
ctrl = _$injector_.get('$controller')(...) // 2
}));
What are the differences and which way is preferred?
Declaring _$controller_ in the parameters of the function passed to inject() makes the framework inject the $controller service in the function.
Calling $injector.get('$controller') explicitly asks the $controller service to the framework.
So it's basically the old "dependency injection vs. factory" debate. Should the framework provide the dependency to the test, or should the test ask it dependency to the framework?
The first one is definitely preferrable in production code: it makes your code testable, and is the way you're supposed to use the framework.
In tests, there is no significant difference, although I prefer the first one too.
There are no differences in an ES5 context. The first one is the suggested way by Angular, as it is used by them and also in their docs and tutorials.
When you pass a function to inject, angular stringifies it and parses fuction parameters to figure out what providers to inject. Then it calls the function with correct providers. For example Angular probably calls $inject.get to get the instance of $controller.
It makes a difference for ES6 because when you stringify an arrow function, it doesn't start with "function(..." so Angular cannot parse it before Angular 1.5. In that case you need to use array notation: inject(['$controller', $controller => { ... }]).
What is the best way to mock localForage.getItem method? I just need to simply return a sample object when localForage.getItem is called.
The first thing is to wrap the third party library in an injectable service so you can swap for a mock implementation in your tests
app.factory('localForage',function(){
//likely coming from global namesapce
return localForage;
});
This then can be injected in directives, services, controller etc
//example with controller
myApp.controller('myCtrl',['localForage',function(localForage){
this.getItem = localForage.getItem;
this.setItem = loaclForage.setItem;
}]);
Now the idea is to replace the normal implementation by a mock in your tests. There are several ways of doing it:
globally: you create a mock module before running your tests which
will define the mock implementation for all your
tests, you will call(beforeEach(module('myMockModule'))) instead of the actual localForage module
on a test suite base: "$provide" a different implementation before you run the tests:
beforeEach(module('myModule',function($provide){
$provide.factory('localForage', function(){
return MockImpl;
});
});
In some cases you can inject directly your mock implementation:
var ctrl;
beforeEach(inject(function($controller){
ctrl = $controller('myCtrl',{localForage:mockImpl});
}))
Now about the mock implementation it depends what you want to do with it (spy it, stub, other things) but the idea is to make it implement the same api than the actual service
var mockImpl = {
getItem : angular.noop,
setItem : angular.noop
}
This simple one will be fine for example if you want to check if the localForage is called and with which arguments (use spyOn(mockImpl, 'getItem'))
I'm trying to compose some unit tests in Karma/Jasmine for a particular module in my project, destination-filters.
Module Decleration:
angular.module('destination-filter', ['ngSanitize']);
My tests fail unless I remove ngSanitize as a dependency. To my understanding that is because when the module is instantiated it will try and pull in that dependency but because in my spec.js file I haven't declared that module it is failing.
Spec File:
describe('Destination Filter Controller', function () {
// Set the variables
var $controller;
var mockNgSanitize;
beforeEach(module('destination-filter'));
beforeEach(function() {
module(function($provide) {
$provide.value('ngSanitize', mockNgSanitize);
});
});
beforeEach(inject(function (_$controller_) {
$controller = _$controller_('DestinationFilterController');
}));
it('should expect the controller to not be null', function() {
// Check the controller is set
expect($controller).not.toBeNull();
});
});
Previously, when mocking out services or functions, the $provide method has proven very useful but I'm not sure my use of it is correct here. I'm assuming $provide used in this way can't mock entire modules but rather services?
To clarify, if I remove the ...['ngSantize'])... from my module deceleration the tests instantiate correctly. The error I am receiving with it left in is Error: [$injector:modulerr] destination-filter
There are three options you could take for using ngSanitize in your tests:
inject the service into your test
stub a method call on ngSanitize
mock the entire ngSanitize service
The option you choose is really dependent on the use of ngSanitize in your working code (not your test code).
Whichever one you go for you need to make the service available in your test, there is no need for $provider (this covers option 1 and there is no need to do any more than this if you just want to make this available to your filter):
beforeEach(module('ngSanitize'));
beforeEach(inject(function(_ngSanitize_) { // the underscores are needed
mockNgSanitize = _ngSanitize_;
}));
Also, make sure that all js files are picked up and loaded by karma. You can define this in karma.conf.js by adding them to the files: property.
2. Stub a method on the service
I like stubs and find them very useful when writing tests. Your tests should only test one thing, in your case a filter. Stubs give you more control over your tests and allow you to isolate the thing under test.
Typically filters, controllers, anything call on lots of other things (services or factories like $http or ngSanitize).
Assuming that your filter is using ngSanitize's $sanitize to sanitize some html you could stub out that method to return sanitized html you have defined to test against your expectations:
// in a beforeEach
spyOn(mockNgSanitize, "$sanitize").and.returnValue('<some>sanitized<html>');
mockNgSanitized.$sanitize(yourDirtyHtml);
See the jasmine docs for more info.
You might have to play around with spying on the right service but this should work ok.
3. Mock the entire service
I don't think you want to go with this option because it will drive you insane figuring out what needs mocking plus mocks can create unrealistic expectations and also, not particularly useful for your use case. If you really want to have a go then something like the below is heading in the right direction (again see the jasmine docs)
beforeEach(function() {
mockNgSanitize = ('ngSanitize', ['linky', '$sanitize', '$sanitizeProvider'];
});
it('mocks the ngSanitize service', function() {
expect(mockNgSanitize.linky).toBeDefined();
});
NB: in all the code above make sure you continue to declare any variables up at the top of your describe block.
I'm using Jasmine to unit test my Angular app. It's pretty easy to test if a method of a function has been called using something like:
spyOn($rootScope, "$emit");
expect($rootScope.$emit).toHaveBeenCalled();
But I can't find a way to check when a function has been called (without a method), for e.g. I'm using $anchorScroll(); in one controller and I have no idea where to apply the above code to this guy. I've seen some Jasmine examples where they were using expect(window.myFunction()).toHaveBeenCalled(), but this doesn't work with Angular's DI.
I can't try it myself at the minute but maybe you could just inject a mock $anchorScroll instead?
var $anchorScroll = jasmine.createSpy('anchorScroll');
$controller('MyCtrl', {
$anchorScroll: $anchorScroll
});
expect($anchorScroll).toHaveBeenCalled();
This should just create a blank spy, one which will take any arguments and do nothing but keep track of the calls for test usage.