4

I'm writing some React/Flux code and using Jest to test it. So far it's been great, except my test are already taking a long time to complete.

The culprit seems to be resetting the mocks in between each test.

My general set up will look something like this:

jest.dontMock('react');
jest.dontMock('../Widget.jsx');

describe('Widget', function() {
  var React, TestUtils, Widget, WidgetStore;

  beforeEach(function() {
    React = require('react/addons');
    TestUtils = React.addons.TestUtils;
    WidgetStore = require('../WidgetStore');
    Widget = require('../Widget');
  });

  it('should fetch initial state from the store', function() {
    WidgetStore.getDoobles.mockReturnValueOnce({});

    var widget = TestUtils.renderIntoDocutment(
      <Widget />
    );

    expect(WidgetStore.getDoobles.mock.calls.length).toBe(1);
  });

  it('should refresh its data when clicked', function() {
    WidgetStore.getDoobles.mockReturnValueOnce({});

    var widget = TestUtils.renderIntoDocutment(
      <Widget />
    );

    WidgetStore.getDoobles.mockReturnValueOnce({'foo': 'bar'});        

    TestUtils.Simulate.click(widget);

    expect(WidgetStore.getDoobles.mock.calls.length).toBe(2);
  });
});

In my example if I don't reload the store in between the two tests I will get the wrong result for the number of calls to getDoobles, which makes sense since it would be the same object.

But reloading the mock takes a bit of time and if I do lots of tests then they end up being slow.

I would love to just clone the object or call a reset function. There is a reset function for an individual function (mockClear()) but there doesn't seem to be a global reset for the whole object. And I can't clone the object because the clone isn't the same object as the one that my React component is accessing, so none of the calls will get registered.

Which brings up the other issue. It seems I need to reload everything in the dependency chain. If I were to just re-require WidgetStore then the object I have access to doesn't seem to be the same object that Widget has access to.

If I just reload WidgetStore and Widget then I will often get errors which seem to be caused by having two copies of React loaded. So I end up having to reload React each time as well.

So is there a better way of doing this?

Community
  • 1
  • 1
Gregory Bell
  • 1,861
  • 1
  • 19
  • 21

1 Answers1

5

We set React and TestUtils variables before describe() and don't use jest.dontMock('react'). Also requires for Widget and WidgetStore could be there. In your case like this:

jest.dontMock('../Widget.jsx');

var React = require('react/addons');
var TestUtils = React.addons.TestUtils;
var WidgetStore = require('../WidgetStore');
var Widget = require('../Widget');

describe('Widget', function() {
  var widget;

  beforeEach(function() {
    widget = TestUtils.renderIntoDocument(
      <Widget />
    );
    WidgetStore.getDoobles.mockClear();
  });

  it('should fetch initial state from the store', function() {
    WidgetStore.getDoobles.mockReturnValueOnce({});

    expect(WidgetStore.getDoobles.mock.calls.length).toBe(1);
  });

  it('should refresh its data when clicked', function() {
    WidgetStore.getDoobles.mockReturnValueOnce({});
    WidgetStore.getDoobles.mockReturnValueOnce({'foo': 'bar'});        

    TestUtils.Simulate.click(widget);

    expect(WidgetStore.getDoobles.mock.calls.length).toBe(2);
  });
});
minterior
  • 381
  • 3
  • 6
  • I don't understand how you can skip telling Jest to not mock React. Seems like everything would fail since all the React functions would be mocks. There's no way for you to know this based on my example, but in my real use case mockClear needs to be above the Widget render because I'm calling the store in the getInitialState call. Which means the render also has to be below the store mocking. Clearing the mock in the beforeEach block works, but I was really looking for a way to reset the entire object. But maybe my I should reorganize my tests to be tighter so I only need to clear a few funcs. – Gregory Bell Feb 16 '15 at 20:51
  • Probably @minterior has specified that React not be mocked in package.json like so: `"jest": { "scriptPreprocessor": "/preprocessor.js", "unmockedModulePathPatterns": [ "/node_modules/react" ] },` – pherris Apr 16 '15 at 15:39