Sinon: Add documentation for how to stub ES6 module dependencies

Created on 23 Mar 2017  路  10Comments  路  Source: sinonjs/sinon

It is a common use case to mock ES modules.

e.g. I have a module that imports getEventByEventId

import getEventByEventId from '../queries/getEventByEventId';

export default async (
  {
    connection,
    session
  }: ResolverContextType
) => {
  if (!session || !session.userId) {
    throw new Error('User must be authenticated.');
  }

  const event = await getEventByEventId(connection, eventId);

  // ..
}

In my test I want to mock getEventByEventId, e.g.

import test from 'ava';
import sinon from 'sinon';
import moment from 'moment';
import createReservation from '../../../src/mutators/createReservation';

test('throws an error if the event is in the past', async (t) => {
  const parameters: any = {};

  const context: any = {
    session: {
      userId: 1
    }
  };

  const stub = sinon.stub().returns({
    date: moment().format('YYYY-MM-DD'),
    time: moment(new Date().getTime() - 1000 * 60).format('HH:mm')
  });

  // How to use the stub to mock `getEventByEventId`?

  await t.throws(createReservation(context), 'Cannot create a reservation for a past event.');
});

Documentation

Most helpful comment

This may be old news, but I do this

import * as myDependency from './some/other/file'

beforeEach(function () {
  this.mySpy = sinon.spy(myDependency, 'default');
});

All 10 comments

To contribute to my own question, the only way I found this to work is by using babel-plugin-rewire, e.g.

import test from 'ava';
import sinon from 'sinon';
import moment from 'moment';
import createReservation from '../../../src/mutators/createReservation';

test('throws an error if the event is in the past', async (t) => {
  const parameters: any = {};

  const context: any = {
    session: {
      userId: 1
    }
  };

  const stub = sinon.stub().returns({
    date: moment().format('YYYY-MM-DD'),
    time: moment(new Date().getTime() - 1000 * 60).format('HH:mm')
  });

  createReservation.__Rewire__('getEventByEventId', stub);

  await t.throws(createReservation(context), 'Cannot create a reservation for a past event.');
});

This solution is far from ideal, though. For one, it breaks the Flow type annotations.

One way to improve a DX would be to provide an ability to define a getter method when stubbing an object, e.g.

const stub = sinon.stub(createReservation, '__Rewire__', 'getEventByEventId');

which would be equivalent to:

const stub = sinon.stub();

createReservation.__Rewire__('getEventByEventId', stub);

I've solved this locally by creating an ad-hoc helper for rewiring the dependencies:

const rewire = (module: any, methodName: string, method) => {
  module.__Rewire__(methodName, method);

  return method;
};

which I am then using like this:

rewire(createReservation, 'getEventByEventId', sinon.stub())
  .returns({
    date: moment().format('YYYY-MM-DD'),
    time: moment(new Date().getTime() - 1000 * 60).format('HH:mm')
  });

Duplicate of #1121

@gajus please see the pointers in #1121. I have tried covering the various strategies for doing this in the comments linked from that.

I think @gajus talked about stubing ES6 module dependence rather than stub ES6 class.
May be #1458 is related.

proxyquire looks like a good choice for this case @gajus , as the function is imported as a module

This may be old news, but I do this

import * as myDependency from './some/other/file'

beforeEach(function () {
  this.mySpy = sinon.spy(myDependency, 'default');
});

It's a bad idea to use sinon to spy or stub into a dependency, while all you need is to mock it.
Proxyquire, jest.mock or rewiremock would be the only right solutions for module-level dependency mocking.

We even have a guide on How to use Link Seams with CommonJS.

Was this page helpful?
0 / 5 - 0 ratings