CallThrough injected spy - javascript

I'm doing some unitTests and my scenario is the following. I have like 50 tests whose call to a service function must be the same, but for one single test It will be so helpfull if I can call the original method. I tried with the and.callThrough but It's not working correctly. I'm trying to override the spy too but I can't. What I'm doing wrong?
beforeEach(inject(function($controller, _myService_){
spyOn(_myService_, 'getSomeData').and.callFake(function(data, params){
return dummyData;
});
createController = function() {
return $controller('MyCtrl',{
$uibModalInstance: modalInstance,
myService: _myService_,
injectedData: injectedData
});
};
}));
This is my test case.
it('My test case', function(){
controller = createController();
controller.myService.getSomeData = jasmine.createSpy().and.callThrough()
});
I'm using jasmine 2.0 and that test case is continuously calling the callFake function.
thanks

jasmine.createSpy().and.callThrough() is unaware of the spied method and there's no way how it can know about it, calling it just results in calling a noop function.
Spying strategy can be changed for existing spies,
controller.myService.getSomeData.and.callThrough();

Related

Angular Unit Test, SpyOn stand-alone function

I am using Jasmine to test an Angular app and would like to test that the getItem() function within my controller is called when the ready() function of the controller is called.
--- Controller ---
var vm = this;
vm.items = [];
$ionicPlatform.ready(ready);
function ready() {
vm.items.push(getItem());
function getItem(){
var item = //do stuff to get item;
console.log('getItem called');
return item;
}
}
--- Spec ---
describe('Controller', function(){
//--- Load app with dependencies and module to test code omitted.
beforeEach(function(){
//How do I spy on getItem() to test that it was called?
//I've tried getItem = jasmine.createSpy()
//I've tried spyOn(window, 'getItem')
}
//--- Initialize the controller and a mock scope code omitted.
beforeEach(function(done){
$ionicPlatform.ready(function(){
done();
});
});
it('getItem function should get called', function(){
expect(getItem).toHaveBeenCalled();
//--- Note, getItem does not get called according to the expect statement,
//--- but does output 'getItem called' to the terminal when running the test.
});
});
Unfortunately, you've come upon a fundamental limit of Javascript unit testing with Jasmine-- you can only spyOn methods that are exposed on some object. If there is a function that is internal to another function, and not exposed in anyway, you cannot test it directly.
However, you do have two options available to you:
Expose the function in a way that it can be spied on (generally as a method of whatever Angular component you are testing).
Test it indirectly.
The first is probably relatively self-evident, but the latter may be a little confusing. Basically, you can't test directly if the getItems function is called, but the function may have downstream methods it calls or values it changes you can test. For instance, you can test that vm.items.push is larger after ready is called, or you can spyOn(console.log) and expect(console.log).toHaveBeenCalledWith('getItem called').
You can find arguments for both approaches on the internet-- I tend to prefer approach two because I don't like doing refactors solely for the purpose of testability, but many will argue that refactoring for testability generally yields better code. That choice is yours to make. Hope this helps!

Testing a function in factory

.factory('Tag', function($window) {
var Context = {};
function reset() {
return Context !== {} ? Context : {};
}
return{
reset:reset
};
})
I have done testing like this
describe('method: reset()', function(){
it('should reset the Context variable', function(){
spyOn(Tag, 'reset').andCallFake(function(){
return Context;
});
expect(Context).toEqual({});
});
afterEach(function(){
if(Context!== {}){
Context = {};
}
})
});
Is, this test is accurate, if yes then why my test coverage is not increasing..
you are calling a fake function not the real one, so the code in your actual function is never executed.
your code coverage tool only marks code that was actually hit.
andCallFake is used to mock an external function which you are not interested in testing and you just want some mock response when the code you are actually testing calls it.
your code should make a real call to ...
Tag.reset()
now if Tag.reset() makes a call to code in another service which you do not want to test then you can use callFake on that call.
Remember this is "unit" testing. The "unit" of code you want to test is the code inside your service, not outside of your service.

AngularJs - How to write testable controllers with private Methods

I'm trying to write a test for one of my controller, using angular.js + jasmine.
Let's say I have a controller
angular.module('app').controller('MyCtrl', function() {
this.myFunc = function() {
// ...
};
activate();
function activate() {
this.myFunc();
}
});
This controller have a function called activate() that is called when the controller is created.
How can I write a test for the activate() function? (like this: when the controller is created, should call a controller function "myFunc()")
I tried to write something like this:
describe('activate() controller', function() {
it('should call function myFunc', inject(function($rootScope, $controller) {
var locals = {$scope: $rootScope.$new()};
var controller = $controller('MyCtrl', locals);
spyOn(controller, 'myFunc').toHaveBeenCalled();
});
}
But I get the error:
Expected spy myFunc to have been called.
I think at the point I create my spy, the controller already called the activate function.
Is there a way to test a controller like this?
The code example you have above executes the myFunc method upon initialization. Therefore, by the time you attach the spy, it has already been executed. The better way of testing would be to inspect what transformations the myFunc has performed.
If the method were part of a service, you could setup your spy in your inject, and then initialize the controller and expect the service method to have been called.

How do I spy on methods called from one class that exist in another class using Jasmine?

I have a method
thisSvc.asyncOperation: function(id) {
return thatSvc.getById(id);
Is it possible to create a spy that will tell me if thatSvc.getById has been called, or is this design an anti-pattern? AFAIK, spies can only be created on a single object.
You can spy on whatever you want, in your jasmine tests just make sure you get that service:
var thisSvc, thatSvc;
beforeEach(inject(function(_thisSvc_, _thatSvc_){
thisSvc = _thisSvc_;
thatSvc = _thatSvc_;
});
it('.asyncOperation should call thatSvc.getById', function(){
spyOn(thatSvc, 'getById');
var id = 4;
thisSvc.asyncOperation(id);
expect(thatSvc.getById).toHaveBeenCalledWith(id);
})
From http://www.htmlgoodies.com/html5/javascript/spy-on-javascript-methods-using-the-jasmine-testing-framework.html#fbid=ib4OX6qA3oS
"spyOn() can only be used when the method already exists on the object. For simple tests, this is your best bet."

How to spy on the original function and not the dummy in Jasmine for javascript testing?

I looked at the other questions regarding spying on functions in Jasmine but I didn't get my doubt answered there. I intend to use andCallThrough to track my original function in my src script. This is what I have:
describe("My Test to spy :", function() {
var mySpy = jasmine.createSpy(window, "login");
beforeEach(function(){
mySpy();
});
it("Expects login() will be called", function(){
expect(mySpy).toHaveBeenCalled();
});
});
So this test passes because its the spy that is being called right? Not the original implementation of the function. So if I use mySpy.andCallThrough() it gives an error. The docs are all about chaining objects and for methods. Nothing for functions. Need some help.
The problem is that you use createSpy instead of spyOn. createSpy will create a new spy so you can't use andCallThrough on it as there is no function to call. Using spyOn will replace an existing function with the spy and save the old function in the spy. So when you use andCallThrough it will call this old method.
You can use createSpy but then you have to pass a name and the original function:
jasmine.createSpy('someName', window.login)
When you use `spyOn', you have to pass the an object holding the function and the name of the function:
jasmine.spyOn(window, 'login')

Categories

Resources