Spying on a constructor using Jasmine

JavascriptJasmineSpy

Javascript Problem Overview


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!

Javascript Solutions


Solution 1 - Javascript

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 - Javascript

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 - Javascript

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 - Javascript

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);
  });
});

Solution 5 - Javascript

My version to test a constructor is to spy on the prototype:

spyOn(flipCounter.prototype, 'setValue').and.callThrough();
var myFlipCounter = new flipCounter("counter", {inc: 23, pace: 500});
expect(flipCounter.prototype.setValue).toHaveBeenCalledTimes(1);

Solution 6 - Javascript

Don't know how to do this using jasmine mocks, but if you want powerful mocking/spy/stubs I recommend sinon.js, wich works very well with jasmine.

From docs:

> A test spy is a function that records arguments, return value, the > value of this and exception thrown (if any) for all its calls. A test > spy can be an anonymous function or it can wrap an existing function. > > Mocks (and mock expectations) are fake methods (like spies) with > pre-programmed behavior (like stubs) as well as pre-programmed > expectations. A mock will fail your test if it is not used as > expected.

With sinon.js you could create a mock of the flipCounter constructor that returns another spy.

Then assert that the constructor was called using constructorMock.calledWithNew(), and assert that the returned spy was called with returnedSpy.calledWith(arg1, arg2...).

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiongerkyView Question on Stackoverflow
Solution 1 - JavascriptggozadView Answer on Stackoverflow
Solution 2 - JavascriptJared DeckardView Answer on Stackoverflow
Solution 3 - JavascriptsurtyaarView Answer on Stackoverflow
Solution 4 - JavascriptStephen VeitView Answer on Stackoverflow
Solution 5 - JavascriptHolgerJerominView Answer on Stackoverflow
Solution 6 - JavascriptebaxtView Answer on Stackoverflow