Notice

╠ This is my personal blog and my posts here have nothing to do with my employers or any other association I may have. It is my personal blog for my personal experience, ideas and notes. ╣
Showing posts with label spyOn. Show all posts
Showing posts with label spyOn. Show all posts

Sunday, February 5, 2017

Spying on JavaScript Method using Jasmine [Part - 2]

Basic Introduction of JavaScript Testing Framework Jasmine

The basic of Jasmine is already discussed in my earlier blog. To read it please click here.


Spying with Jasmine

One of the basic reason of spying is to do unit testing of method in isolation. So we mock the method to bypass other dependencies.  

Let's see what Jasmine offer us.

Spy on a method 

spyOn(object, methodName) it can only exist inside the describe or it block. In another words it can only be define inside spec or suit, and it will be removed once spec get existed.  

spyOn(obj , 'setName');

When you want to invoke the actual method but call will be tracked.

spyOn(obj , 'setName').and.callThrough();

When you call and.callThrough, the spy acts as a proxy, calling the real function, but passing through a spy object allowing you to add tests like expectation.

When you want to remove the effect of spyOn(obj , 'setName').and.callThrough()

spyOn(obj , 'setName').and.stub() removes the effect of
spyOn(obj , 'setName').and.callThrough() on a spy.

When you want to return a specific value on all calls.

spyOn(obj, "getName").and.returnValue('Andy');

When you want to invoke a custom function.

spyOn(obj, "getName").and.callFake(function(arguments, here) { return 'Andy'; });

When you want to throw specific error.

spyOn(obj, "setName").and.throwError("myError");




Every call to a spy is tracked and exposed on the calls property.


Calls Purpose
any() returns false if the spy has not been called at all, and then true once at least one call happens.
expect(obj.setName.calls.any()).toEqual('Andy');
count() returns the number of times the spy was called.
expect(obj.setName.calls.count()).toEqual(2);
argsFor(index) returns the arguments passed to call number index.
obj.setName('Andy'); expect(obj.setName.calls.argsFor(0)).toEqual(["Andy"]);
allArgs() returns the arguments to all calls.
obj.setName('Andy'); 
obj.setName('Anindya','Banerjee'); 
expect(obj.setName.calls.allArgs()).toEqual([["Andy"],["Anindya","Banerjee"]]);
all() returns the context (the this) and arguments passed all calls.
obj.setName('Andy'); 
expect(obj.setName.calls.all()).toEqual({object: obj, args: ["Andy"]});
mostRecent() returns the context (the this) and arguments for the most recent call.
obj.setName('Andy'); 
obj.setName('Anindya','Banerjee'); 
expect(obj.setName.calls.mostRecent()).toEqual({object: obj, args:"Anindya","Banerjee"});
first() returns the context (the this) and arguments for the first call.
obj.setName('Andy'); 
obj.setName('Anindya','Banerjee');
expect(obj.setName.calls.first()).toEqual({object: obj, args:"Andy"});

reset()
clears all tracking for a spy.
obj.setName.calls.reset();

There is a property named object which actually return the this object when the spy was call. This can be used with all/mostReset/first.
Please note here these methods are return an array of json object and one of key is object. Please refer to the above table.

Here is an example.

obj.setName('Andy');
obj.setName('Anindya','Banerjee');
expect(obj.setName.calls.all().object).toEqual(obj);
expect(obj.setName.calls.mostRecent().object).toEqual(obj);
expect(obj.setName.calls.first().object).toEqual(obj);



There are three matchers toHaveBeenCalledtoHaveBeenCalledWith and toHaveBeenCalledTimes.

toHaveBeenCalled return true if the spy was called.

expect(obj.setName).toHaveBeenCalled();


toHaveBeenCalledWith return true if the argument list matches any of the recorded calls to the spy.


expect(obj.setName).toHaveBeenCalledWith('Andy');

toHaveBeenCalledTimes return true if the spy was called the specified number of times.

expect(obj.setName).toHaveBeenCalledTimes(2);


What we will do if we didn't have a function to spy on ?

Jasmine provide functions to deal with this kind of situations. 

Function Purpose
jasmine.createSpy create a spy function which doesn't exist.
var dummyFunction = jasmine.createSpy('dummy function'); $('#mybutton').click(dummyFunction);
jasmine.createSpyObj create multiple spy function which doesn't exist.
// Suppose you got a Json object from back-end // Person is a spy object with getName and getAge methods and an id property var person = jasmine.createSpyObj("person", ["getName", "getAge"]); person.id = 1234;
jasmine.any returns true if the constructor matches the constructor of the actual value.
expect({}).toEqual(jasmine.any(Object)); expect(12).toEqual(jasmine.any(Number));
jasmine.anything returns true if the actual value is not null or undefined.
expect({}).toEqual(jasmine.anything());
jasmine.objectContaining when an expectation only cares about certain key/value pairs in the actual.
var person = { firstName : "Anindya", lastName : "Banerjee", aliasName : "Andy" }; expect(person).toEqual(jasmine.objectContaining({ aliasName: "Andy" }));
jasmine.arrayContaining when an expectation only cares about some of the values in an array.
var num = [1, 2, 3, 4]; expect(num).toEqual(jasmine.arrayContaining([3, 1])); expect(num).not.toEqual(jasmine.arrayContaining([6]));
jasmine.stringMatching When match a portion of a string in a spy expectation.
expect({names: 'AndyAnindya'}).toEqual({names: jasmine.stringMatching('Andy')});