Angular + Jasmine: beforeEach() syntax with module() - angularjs

Can you please explain how this line works:
beforeEach(module('phonecatApp'));
beforeEach() expects a callback function to call before each test.
module() returns an angular.Module object.
What does beforeEach() do with an object?

If you look in the source of angular.mock.module you can see it either returns a function, or the result of a function, depending on whether a spec is running:
window.module = angular.mock.module = function() {
var moduleFns = Array.prototype.slice.call(arguments, 0);
return isSpecRunning() ? workFn() : workFn;
/////////////////////
function workFn() {
...
When beforeEach is called, I suspect this is treated as not being during a spec, so the function returns a callback that runs when the test runner later calls the callbacks registered with beforeEach.
Also I don't see documented, or in the source, that it actually returns a module object. It apparently registers a module with the dependency injection system.

Related

Make jasmine test wait until dependent method call's promise is resolved

I have to test a void method which has a dependent method call that returns a promise and I can't mock that call as it is made on a local object created inside the tested method.
Is there a way to make jasmine expect calls to wait until the promise is resolved? I tried using $rootScope.$digest() but it is not ensuring that the dependent call's promise is resolved.
EDIT: Adding sample code
module.service('serviceToBeTested', ['$rootScope', 'someOtherService',
function($rootScope, someOtherService) {
var thirdPartyLib;
function fnToBeTested() {
//some validations and filtering on rootScope variable to build input for processing
thirdPartyLib = new ThirdPartyLib(); //this is not an angular service
var anotherFunction = function() {
//some hook functions that will be triggered by the third party library
}
// anotherFunction is set into thirdPartyLib so that hook functions will be triggered
thirdPartyLib.start().then(funtion() {
thirdPartyLib.someThing.load(); //this load will trigger one hook function
}
}
}]);
What I need to verify is that, upon invoking fnToBeTested(), a particular logic inside a hook function is executed (for that the control has to go inside the then part of thirdPartyLib.start()).
Actually this gets executed but only after the expect statements in the spec are executed.
And my spec file is almost like this:
it('should do this and this', function() {
// some initialization
serviceToBeTested.fnToBeTested();
$rootScope.$digest();
//expect statements
});
EDIT 2: Adding details on trial made as Andrew suggested below and adding clarity on how instance is created
ThirtPartyLib is instantiated inside main source as:
var theLib = require('theLib');
...............................
thirdPartyLib = new theLib.ThirdPartyLib();
And in spec, I created a var just like this and spied prototype as below:
var theLib = require('theLib');
................................
spyOn(theLib.ThirtPartyLib.prototype, 'start').and.callFake(.....);
But the fake function is not reached. When I check theLib.ThirtPartyLib.prototype in spec during debug, it lists the SpyStrategy while checking theLib.ThirtPartyLib.prototype in main source, it doesn't list that.
You should be able to test this with some clever use of mocking. In your beforeEach block, you can do something like this:
let promise; // declare this outside of your beforeEach so you have access to it in the specs
promise = $q.resolve(); // assign promise to
spyOn(ThirdPartyLib.prototype, 'start').and.returnValue(promise);
And then in your test, you now have access to the promise returned by start.

How to use $timeout effectively in Angular?

I have the following code that doesn't work:
$scope.function_A();
$scope.function_B();
and this DOES work:
$scope.function_A();
$timeout(function(){
$scope.function_B();
}),100;
This is due to the fact that function_B refers to a directive that hasn't been created yet by Angular. I believe that's why using $timeout fixes the problem.
My issue is: how to be sure that the 100 millisecond timeout is correct and will always work? Is it better to somehow detect that function_A finished instead of using $timeout?
You can use Promises.
If you require function A to finish it's work, before calling function B, it is a good idea to make it return a promise. Some angular services have methods that already return a Promise, e.g. $http.get. By using $q you can prepare your own promises. For example:
$scope.function_A = function() {
//function code
return $q.when();
}
$scope.function_A().then(function() {
$scope.function_B();
});
Read more about $q and Promises here
Use the callback mechanism
$scope.function_A(callback){
// your implementation
callback() // add it at the end
}
Now call function_B inside function_A
$scope.function_A(function(){
$scope.function_B();
});

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.

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.

Do Angular Directive Factory Functions Get Invoked as Constructors of Functions

In Angular, when registering a directive to a module, does the directive factory function get invoked using new or just with simple function call?
eg.
var MyDirective = function() {
return {
link: function() { ... }
};
}
module('myMod', []).directive('myDirective', MyDirective);
Does MyDirective get called internally as:
... = MyDirective();
or as
... = new MyDirective();
The Angular Guide on providers states:
Earlier we mentioned that we also have special purpose objects that
are (...) are Controller, Directive, Filter and Animation.
The instructions for the injector to create these special objects
(with the exception of the Controller objects) use the Factory recipe
behind the scenes.
This fact be clearly seen in compile.js source code. And because we know that factory recipe in Angular simply invokes the function (with its dependencies, via $injector.invoke(fn)) so the correct answer to your question is ... = MyDirective();
It is invoked using an $injector, i.e. $injector.invoke(MyDirective), so that dependencies can be resolved and injected. Internally, $injector.invoke call MyDirective(), without the new, and pass in the dependencies as arguments.

Resources