Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add documentation for how to stub ES6 module dependencies #1358

Closed
gajus opened this issue Mar 22, 2017 · 10 comments
Closed

Add documentation for how to stub ES6 module dependencies #1358

gajus opened this issue Mar 22, 2017 · 10 comments
Assignees

Comments

@gajus
Copy link

gajus commented Mar 22, 2017

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.');
});
@gajus
Copy link
Author

gajus commented Mar 22, 2017

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.

@gajus
Copy link
Author

gajus commented Mar 22, 2017

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

@gajus
Copy link
Author

gajus commented Mar 22, 2017

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

@fatso83 fatso83 self-assigned this Mar 23, 2017
@fatso83
Copy link
Contributor

fatso83 commented Mar 23, 2017

Duplicate of #1121

@fatso83
Copy link
Contributor

fatso83 commented Mar 23, 2017

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

@chenzhutian
Copy link

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

@zhenyulin
Copy link

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

@whawker
Copy link

whawker commented May 24, 2018

This may be old news, but I do this

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

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

@theKashey
Copy link

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.

@mroderick
Copy link
Member

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

@sinonjs sinonjs locked and limited conversation to collaborators Mar 19, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

7 participants