unit test this.$onDestroy in angular>1.5 / es6 - angularjs

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));

Related

Unit Testing Angular Function using Karma Jasmine

I have a function like this inside a controller
$scope.closeMenu = function () {
$('#menu').hide();
};
If a functions returns a value I am able to test using
expect($scope.value).toEqual();
How do we test the above function using jasmine. Please help thanks
If a functions returns a value I am able to test using expect($scope.value).toEqual(); How do we test the above function using jasmine
You should rewrite your function, so as it only set a variable of the model. Then it will be testable using the way you already know in Jasmine. One of the point of angular is that you don't manipulate the DOM from the controller. Providing you follow these guidelines, controllers are far easier to test.
Moreover, in your case rewriting is very straightforward:
$scope.closeMenu = function () {
$scope.isOpen = false;
};
Template:
... id="menu" ng-show="isOpen" ...
In case you still need to test some characteristics of a DOM element, for example it's visibility, jasmine-jquery might be useful for you. Example:
expect($('#menu')).toBeHidden()

Mock 'document' with Jasmine

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.

AngularJS unit test check if function without methods have been called

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.

AngularJS: how to "create" controller in unit test, and why scope is not defined?

everywhere in application my company created we used this example of how to create a controller :
app.myFunnyController = function($scope.....){}
but i see that everywhere in test people are using this way of creating controllers:
app.controller('myFunnyController', function ($scope) {
}
And i can see that when i am creating my test and using app.myFunnyController declaration:
'use strict';
describe('publicCtrl', function(){
beforeEach(module('app'));
it("should be true", inject(function($controller){
var scope = {},
ctrl = $controller('myFunnyController', {$scope : scope});
expect(scope.data).toBe("test2");
}));
})
I getting an error of myFunnyController is not a function. If i using the second type of declaration, everything works fine. Why does this happend?
An other problem is that i am getting error: scope is not defined.
I am new to Karma and Unit testing for front end, what am i doing wrong?
From my understanding, the second syntax (app.controller(...)) registers the controller function on the module. The first syntax just adds the controller function as an attribute of the module. app.controller does a bit more magic under the hood so that when you call $controller('myFunnyController, ...) in your test, the module knows about the controller and can run it. This is the recommended way to define controllers according to the angular controllers guide.

How to be aware that when a function is run in angular context?

Say I have an service:
angular.module("app").factory("myService", function($rootScope){
return {
doSomething: function(){
console.log('doingSomething', $rootScope.$$phase);
$rootScope.someVar = true;
}
}
});
If I run it inside the controller like this
angular.module("app").controller("HomeController", function(myService){
myService.doSomething();
});
The console log gives:
doingSomething $apply
But if I run the service inside a unit test environment
it('should doSomething', inject(function(myService) {
myService.doSomething();
}));
The console log only get
doingSomething null
The typical answer that if you need to trigger the $digest cycle yourselves through $apply(). The angular js wiki mentioned about ng-click, $timeout and $http, but surely there are other places, such as running inside the controller. How can I determine that without trial and error?
It really depends on what you are testing. If you are testing something that requires a digest cycle (like $broadcast or $on events), then you will need to call scope.$apply() or simply scope.$digest() in the setup of the test.
However, most of the time, this is not required because services are usually designed as discrete pieces of functionality that do not require the scope. In the case of an HTTP call, the $httpBackend` service allows you to mock out the response.
For ng-click, you shouldn't be using this is a service. And for a controller you'll be binding to a function that you can test in isolation.
Hope this helps.

Resources