Javascript: Mocking Constructor using Sinon

JavascriptUnit TestingConstructorMockingSinon

Javascript Problem Overview


I am pulling my hair out trying to figure out how to mock a constructor using sinon. I have a function that will create multiple widgets by calling a constructor that accepts a few arguments. I want to verify that the constructor is called the correct number of times with the correct parameters, but I don't want to actually construct the widgets. The following links seemingly explain a straightforward way of mocking the constructor, however it does not work for me:

https://stackoverflow.com/questions/9347631/spying-on-a-constructor-using-jasmine

http://tinnedfruit.com/2011/03/25/testing-backbone-apps-with-jasmine-sinon-2.html

When I make the following call to stub the constructor:

sinon.stub(window, "MyWidget");

I get the following error:

Uncaught TypeError: Attempted to wrap undefined property MyWidget as function 

When debugging in Chrome I see MyWidget shows up in the Local section of the Scope Variables, however there is not MyWidget property off of window.

Any help would be greatly appreciated.

Javascript Solutions


Solution 1 - Javascript

I needed a solution for this because my code was calling the new operator. I wanted to mock the object that the new call created.

var MockExample = sinon.stub();
MockExample.prototype.test = sinon.stub().returns("42");
var example = new MockExample();
console.log("example: " + example.test()); // outputs 42

Then I used rewire to inject it into the code that I was testing

rewiredModule = rewire('/path/to/module.js');
rewiredModule.__set__("Example", example);

Solution 2 - Javascript

From the official site of sinonjs:

> Replaces object.method with a stub function. The original function can be restored bycalling object.method.restore(); (or stub.restore();). An exception is thrown if the property is not >already a function, to help avoid typos when stubbing methods.

this simply states that the function for which you want to create the stub must be member of the object object.

To make things clear; you call

sinon.stub(window, "MyWidget");

The MyWidget function needs to be within the global scope (since you pass window as parameter). However, as you already said, this function is in a local scope (probably defined within an object literal or a namespace).

In javascript everyone can have access to the global scope, but not the other way around.

Check where you declare the MyWidget function and pass container object as first parameter to sinon.stub()

Solution 3 - Javascript

Using Sinon 4.4.2, I was able to mock an instance method like this:

const testObj = { /* any object */ }
sinon.stub(MyClass.prototype, "myMethod").resolves(testObj)
let myVar = await new MyClass(token).myMethod(arg1, arg2)
// myVar === testObj

A similar solution provided here: https://stackoverflow.com/questions/21072016/stubbing-a-class-method-with-sinon-js

Solution 4 - Javascript

I used Mockery to Mock a Constructor/Function without any problems.

var mockery = require('mockery');
var sinon = require('sinon');

mockery.enable({
  useCleanCache: true,
  warnOnReplace: false,
  warnOnUnregistered: false
});

exports.Client = function() {/* Client constructor Mock */};
var ClientSpy = sinon.spy(exports, 'Client');
mockery.registerMock('Client', ClientSpy);

var Factory = require('Factory'); // this module requires the Client module

You should be able to apply a Sinon Spy just as the example above does.

Make sure to disable or reset Mockery after the test(s)!

Solution 5 - Javascript

Use sinon.createStubInstance(MyES6ClassName), then when MyES6ClassName is called with a new keyword, a stub of MyES6ClassName instance will returned.

Solution 6 - Javascript

Just found this in the documentation.

>If you want to create a stub object of MyConstructor, but don’t want the constructor to be invoked, use this utility function. > >var stub = sinon.createStubInstance(MyConstructor)

Solution 7 - Javascript

I ran into this error by mistakenly typing sinon.stub.throws(expectedErr) rather than sinon.stub().throws(expectedErr). I've made similar mistakes before and not encountered this particular message before, so it threw me.

Solution 8 - Javascript

Mocking and stubbing the constructor with sinon

I give two solutions. The first addresses the question, how to mock the constructor with behaviour, the second shows how to stub it with a dummy. Google guided me multiple times to this question for the search how to stub it.

Mocking the constructor with behaviour

I don't know if this is the shortest path. At least it does, what was asked for.

First I use sinon's fake method to create a mocking constructor Mock with the behaviour I want. Then I have to add the methods one by one. For reasons I didn't investigate, it did not work, when setting the whole prototype of UnderTest to Mock.

require('chai').should();
const { fake} = require('sinon');

class UnderTest {
  constructor() {
    this.mocked = false;
  }

  isMocked() {
    return this.mocked;
  }
}

describe('UnderTest', () => {
  let underTest;
  let isMocked;
  before(() => {
    const Mock = fake(function () { this.mocked = true; });
    Mock.prototype.isMocked = UnderTest.prototype.isMocked;
    underTest = new Mock();
    isMocked = underTest.isMocked();
  });
  it('should be mocked', () => {
    isMocked.should.be.true;
  });
});

Stubbing the constructor with a dummy

If you are leaded to this post, because you just want to stub the constructor to keep it from being executed.

Sinon's createStubInstance creates a stubbed constructor. It also stubs all methods. Hence, the method under test has to be restored before.

require('chai').should();
const { createStubInstance } = require('sinon');

class UnderTest {
  constructor() {
    throw new Error('must not be called');
  }

  testThis() {
    this.stubThis();
    return true;
  }

  stubThis() {
    throw new Error('must not be called');
  }
}

describe('UnderTest', () => {
  describe('.testThis()', () => {
    describe('when not stubbed', () => {
      let underTest;
      let result;
      before(() => {
        underTest = createStubInstance(UnderTest);
        underTest.testThis.restore();
        result = underTest.testThis();
      });
      it('should return true', () => {
        result.should.be.true;
      });
      it('should call stubThis()', () => {
        underTest.stubThis.calledOnce.should.be.true;
      });
    });
  });
});

Solution 9 - Javascript

I was able to get StubModule to work after a few tweaks, most notably passing in async:false as part of the config when requiring in the stubbed module.

Kudos to Mr. Davis for putting that together

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
QuestionsevenstripeView Question on Stackoverflow
Solution 1 - JavascriptstarmerView Answer on Stackoverflow
Solution 2 - JavascriptppolianiView Answer on Stackoverflow
Solution 3 - JavascripttheUtherSideView Answer on Stackoverflow
Solution 4 - Javascriptuser3331119View Answer on Stackoverflow
Solution 5 - JavascriptYilingView Answer on Stackoverflow
Solution 6 - JavascriptAXMIMView Answer on Stackoverflow
Solution 7 - Javascriptuser1978019View Answer on Stackoverflow
Solution 8 - JavascriptBlcknxView Answer on Stackoverflow
Solution 9 - JavascriptsevenstripeView Answer on Stackoverflow