Spying on a constructor using Jasmine
I am using Jasmine to test if certain objects are created and methods are called on them.
I have a jQuery widget that creates flipcounter objects and calls the setValue method on them. The code for flipcounter is here: https://bitbucket.org/cnanney/apple-style-flip-counter/src/13fd00129a41/js/flipcounter.js
The flipcounters are created using:
var myFlipCounter = new flipCounter("counter", {inc: 23, pace: 500});
I want to test that the flipcounters are created and the setValue method is called on them. My problem is that how do I spy on these objects even before they are created? Do I spy on the constructor and return fake objects? Sample code would really help. Thanks for your help! :)
Update:
I've tried spying on the flipCounter like this:
myStub = jasmine.createSpy('myStub');
spyOn(window, 'flipCounter').andReturn(myStub);
//expectation
expect(window.flipCounter).toHaveBeenCalled();
Then testing for the setValue call by flipCounter:
spyOn(myStub, 'setValue');
//expectation
expect(myStub.setValue).toHaveBeenCalled();
the first test for initializing flipCounter is fine, but for testing the setValue call, all I'm getting is a 'setValue() method does not exist' error. Am I doing this the right way? Thanks!
Solution 1:
flipCounter
is just another function, even if it also happens to construct an object. Hence you can do:
var cSpy = spyOn(window, 'flipCounter');
to obtain a spy on it, and do all sorts of inspections on it or say:
var cSpy = spyOn(window, 'flipCounter').andCallThrough();
var counter = flipCounter('foo', options);
expect(cSpy).wasCalled();
However, this seems overkill. It would be enough to do:
var myFlipCounter = new flipCounter("counter", options);
expect(myFlipCounter).toBeDefined();
expect(myFlipCounter.getValue(foo)).toEqual(bar);
Solution 2:
I would suggest using jasmine.createSpyObj()
when you want to mock objects with properties that need to be spied on.
myStub = jasmine.createSpyObj('myStub', ['setValue']);
spyOn(window, 'flipCounter').andReturn(myStub);
This tests interactions with the expected flipCounter
interface, without depending on the flipCounter
implementation.
Solution 3:
The following does not rely on 'window'. Lets say this is the code you want to test -
function startCountingFlips(flipCounter) {
var myFlipCounter = new flipCounter("counter", {inc: 23, pace: 500});
}
Your test could be -
var initSpy = jasmine.createSpy('initFlipCounter');
var flipCounter = function(id, options) {
initSpy(id, options);
}
startCountingFlips(flipCounter);
expect(initSpy).toHaveBeenCalledWith("counter", {inc:23, pace:500});
Solution 4:
You have to implement a fake constructor for flipCounter
that sets the setValue
property to a spy function. Let's say the function you want to test is this:
function flipIt() {
var myFlipCounter = new flipCounter("counter", {inc: 23, pace: 500});
myFlipCounter.setValue(100);
}
Your spec should look like this:
describe('flipIt', function () {
var setValue;
beforeEach(function () {
setValue = jasmine.createSpy('setValue');
spyOn(window, 'flipCounter').and.callFake(function () {
this.setValue = setValue;
});
flipIt();
});
it('should call flipCounter constructor', function () {
expect(window.flipCounter)
.toHaveBeenCalledWith("counter", {inc: 23, pace: 500});
});
it('should call flipCounter.setValue', function () {
expect(setValue).toHaveBeenCalledWith(100);
});
});