Unit Testing Angular Function using Karma Jasmine - angularjs

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

Related

What is the difference between calling _$controller_ instead of _$injector_.get('$controller')?

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 => { ... }]).

Why angular module's factory method should be redefined in the test mock

in an angular.js tutorial , i found this example of a factory testing :
the module :
angular.module('omdbModule', [])
.factory('omdbApi', function omdbApiFactory() {
return {
search: function() {
//logic to get data return movieData;
}
}
the test (it can be made by two ways : passing an anonymous function with '$provide' as an argument :
angular.mock.module(function($provide){
$provide.factory('omdbApi', function(){
return{
search:function(query){
return movieData;
}
}
});
or just using an object literal :
angular.mock.module({
'omdbApi':{
{ search:function(query){
return movieData;
}
}
});
in both cases i don't understand why the search function had to be redefined in the mock instead of just getting the factory and then automatically access all its properties and methods. I think defining the whole factory in a mock is good in case it doesn't really exist in the module but why this is done when the module and its factory are already defined and they really exist?
One of the reasons to mock a service is to prevent undesirable actions that would require fixtures (non-$http XHR requests, DOM operations) or have too much moving parts.
Another good reason is to prevent test cross-contamination. If unit A breaks, the developer may want to know what exactly is broken. It becomes harder if B and C unit tests are also red, so the one should follow the breadcrumb to figure out if the troublemaker is A. It becomes much harder if the breadcrumb stops and the one ends up debugging both app and specs instead of fix-and-go type of work.
Unless the test has for an object to test several units together (which is integration test, it complements unit tests and do not replaces them), test each unit in isolation.

Jasmine: How to test a controller function calling other javascript function

Here is the sample code:
App.controller('Crtl', ["$scope", function ($scope) {
$scope.FetchDetail = function () {
var accNum = this.customer.accNo;
GetAccountDetails(accNum);
}; }]);
I am new to Jasmine and right now I am writing unit tests for my angular code. Here I created FetchDetail function which then calls javascript function GetAccountDetails(accNum).
How can I test this sample using Jasmine.
It depends if you need to stub it (i.e. capture and change its behaviour) or if it is enough to spy on it (watch it). Either way, you would be better off injecting it, so you can control it.
I have been using sinon http://sinonjs.org quite happily.
If you want to spy on it, i.e. watch its behaviour without changing it, then before calling it, you would do
var spy = sinon.spy(GetAccountDetails);
Then you can check if it was called, or what arguments were, etc.
spy.calledOnce === true; // if it was called once
spy.called === true; // if it was called at all
spy.firstCall.args[0]; // first argument to first call
// etc. - check out the docs
If you need to stub it, use the stub functionality, where you can then define what the behaviour should be. You get all of the spying features, but also control what the response is. However, it is hard to spy on a global, as opposed to a method of an existing object.

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.

Mocking Angulars ng-init function in a Jasmine test

I would like to inject this function into the ng-init directive when I instantiate a controller in an angular test, in the html I would set it up like this:
<div ng-controller="my-ctrl" ng-init="initialize({updateUrl: 'list_json'})
In my test I could just write a function like this which would stick the correct stuff in the scope but I think it is messy. Also I am worried about the function currently hard-wired in the html messing it up in my midway test
function initController($scope){
$scope['updateUrl'] = 'list_json'
}
I don't think that ng-init creates any functions by itself, so I think it's just a wrong way of using it (judged by your snippets).
What I usually do is create the init function myself and then test it without a problem in the controller's unit test.
<div ng-controller="my-ctrl" ng-init="init({ updateUrl : 'list_json' })">...</div>
function MyController($scope){
$scope.init = function(config) {
$scope.updateUrl = config.updateUrl;
}
}
Then in Jasmine you can without a problem write a test which would call the init() function (boilerplate skipped):
// Given
var config = { updateUrl : 'list_json' };
// When
myController.init(config);
// Then
// ...
Important comment about Unit Testing
If you think that you should be testing if your HTML markup is calling init() with given parameters, then I think you are wrong. There is simply nothing to test. There is no logic there at all! Your tests should be verifying that components work how they should work, if programmer will make a mistake with passing wrong parameters from HTML to Angular, then you shouldn't be bothered with covering such mistakes in unit tests. You can't force people not to make mistakes :-)

Resources