34

I'd like to unit test the following ES6 class:

// service.js
const InternalService = require('internal-service');

class Service {
  constructor(args) {
    this.internalService = new InternalService(args);
  }

  getData(args) {   
    let events = this.internalService.getEvents(args);
    let data = getDataFromEvents(events);
    return data;
  }
}

function getDataFromEvents(events) {...}

module.exports = Service;

How do I mock constructor with Sinon.JS in order to mock getEvents of internalService to test getData?

I looked at Javascript: Mocking Constructor using Sinon but wasn't able to extract a solution.

// test.js
const chai = require('chai');
const sinon = require('sinon');
const should = chai.should();

let Service = require('service');

describe('Service', function() {
  it('getData', function() {
    // throws: TypeError: Attempted to wrap undefined property Service as function
    sinon.stub(Service, 'Service').returns(0);
  });
});
3

2 Answers 2

27

You can either create a namespace or create a stub instance using sinon.createStubInstance (this will not invoke the constructor).

Creating a namespace:

const namespace = {
  Service: require('./service')
};

describe('Service', function() {
  it('getData', function() {
    sinon.stub(namespace, 'Service').returns(0);
    console.log(new namespace.Service()); // Service {}
  });
});

Creating a stub instance:

let Service = require('./service');

describe('Service', function() {
  it('getData', function() {
    let stub = sinon.createStubInstance(Service);
    console.log(stub); // Service {}
  });
});
Sign up to request clarification or add additional context in comments.

5 Comments

thanks! and how do I add a mock of this.internalService.getEvents returning a string?
In this answer stub will have each method from internalService as a sinon.stub. So you can do this: stub.getEvents.returns("some string")
@sdgluck I figured it out. This is what I needed let stub = sinon.createStubInstance(Service); stub.internalService = { getDataFromEvents: function() { return 'test'; } }; expect(stub.getData()).to.equal(...);
@krl I don't think you are doing things the right way now. You are now just overriding the property 'internalService' after the instance is created. This is not stubbing InternalService with sinon as you probably would want to do.
The Creating a namespace: suggestion in answer provided above worked like a charm for me. I only had to make sure that in the code being tested as well that I imported via the name space import pattern suggested above: Changed const { FuncName } = require('..') to const nameSpace = require('..'), and in the rest of the code prefixed FuncName with nameSpace.FuncName wherever it gets invoked with the new keyword when creating objects: new nameSpace.FuncName(...) instead of new FuncName(...). In my test JS I create spy like this: `sinon.spy(nameSpace, "FuncName").
0

You could also use proxyquire:

const proxyquire = require('proxyquire');
const sinon = require('sinon');

describe('Service', function() {
    it('getData', function() {
        const internalServiceStub = {
            getEvents: sinon.stub() // mock however you wish
        };
        const InternalServiceStub = sinon.stub().callsFake(() => internalServiceStub);
        const ServiceStub = proxyquire('./service', {
            "./internal-service": InternalServiceStub
        });

        const serviceStub = new ServiceStub();
        // call serviceStub.getData(...) with mocked internalService
    });
});

Notice that this solution will not invoke the constructor.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.