Mocking Angulars ng-init function in a Jasmine test - angularjs

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

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

Is it an anti pattern to use ng-init to run a function on the controller when view is loaded

To make testing easier I have been initiating my controllers with ng-init
so that in my controller I have code like this
$scope.initiate = function () {
myResource.makeXhrRequest();
}
and my view code looks like this
<div data-ng-init=initiate()"> ALL MY HTML IN HERE </div>
the benefit is that when I unit test I can have carefully test that myResource.makeXhrRequest(); is called.
Without this approach I find that I have to mock up the xhrRequest on every test.
My question is "Is this considered an anti pattern?"
I personally only use ng-init when i want to reuse a controller/directive, but i want some view specific initialization to be made. i don't think is an anti-pattern as long as you don't abuse it

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.

AngularJS how to unit test to ensure directives like ng-click point to valid code

I'm using Jasmine to write unit tests for our controllers, but wanted to get community feedback on how to handle this situation...
I have a controller - InvoiceController, like this:
angular.module('myModule').controller('myController', ['$scope',
function($scope) {
$scope.doSomething = function() {
$scope.something = 'bar';
};
}
]});
In my unit tests I verify that my controller has the expected methods:
it('should be able to do some work', function() {
// initialize scope properties
scope.someProperty = 'foo';
// load controller using those properties
injectController();
// do I have all of the functions necessary to do this work?
expect(typeof (scope.doSomething)).toBe('function');
// now execute test
scope.doSomething();
expect(scope.something).toBe('bar');
}
And finally, in my html I have an element with an ng-click, like this:
<button ng-click="doSomehing()">Do Something</button>
Looks good, right? BUT, did anyone catch what I did wrong?
My ng-click method is misspelled, but all tests are green and life seems rosy...until I try to click on that guy and nothing happens. No render time error, no error on click. Hmm.
Several times now as I'm refactoring code this has got me. I rename doSomething to doSomethingCooler in the unit test and in the controller but miss a place in the html. After a minute of head scratching I see what was missed.
I'd love a way to ensure that the markup is valid. E2E tests seem to be the obvious solution, but those are prone to fragility so we are hoping there are some alternatives.
If this were ASP.Net I would attach the click events from code behind so that I would get compile time errors vs run time errors.
Thoughts??
Thad
One thing you could do is get the template text and run $compile on it. And then link it to your controller's scope. Then you could do something like dom.find('[ng-click]').click();, which should throw if any of them is not defined in your controller's scope. This is similar to how you generally test directives.

Resources