Using Jest mocks results in "Actions must be plain objects. Use custom middleware for async actions."

Kevin picture Kevin · Apr 20, 2018 · Viewed 10.6k times · Source

I have several Redux-Thunk-style functions that dispatch other actions in one file. One of these actions dispatches the other as part of its logic. It looks similar to this:

export const functionToMock = () => async (dispatch) => {
    await dispatch({ type: 'a basic action' });
};

export const functionToTest = () => async (dispatch) => {
    dispatch(functionToMock());
};

In the case I'm actually running into, the functions are both much more involved and dispatch multiple action objects each. As a result, when I test my real-world functionToTest, I want to mock my real-world functionToMock. We already test functionToMock extensively, and I don't want to repeat the logic in those tests in functionToTest.

However, when I try that, like so:

import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';

jest.mock('../exampleActions');
const actions = require('../exampleActions');

const mockStore = configureMockStore([thunk]);

describe('example scenario showing my problem', () => {
    test('functionToTest dispatches fuctionToMock', () => {
        actions.functionToMock.mockReturnValue(() => Promise.resolve());

        const store = mockStore({});

        store.dispatch(actions.functionToTest());

        expect(actions.functionToMock.mock.calls.length).toBe(1);
    });
});

I get this error:

 FAIL  test.js
  ● example scenario showing my problem › functionToTest dispatches fuctionToMock

    Actions must be plain objects. Use custom middleware for async actions.
        at Error (native)

      at dispatch (node_modules\redux-mock-store\dist\index-cjs.js:1:3137)
      at Object.dispatch (node_modules\redux-thunk\lib\index.js:14:16)
      at Object.<anonymous> (test.js:15:23)

(The example code I posted actually produces this error if you set them up in an environment with Jest, Redux, and Redux-Thunk. It is my MVCE.)

One thought I had is that I can move the two functions into different files. Unfortunately, doing so would break pretty dramatically with how the rest of our project is organized, so I'm not willing to do that unless it is truly the only solution.

How can I mock functionToMock in my tests for functionToTest without getting this error?

Answer

Kevin picture Kevin · Apr 24, 2018

One solution is just to mock functionToMock. This question and its answers explain how to do so: How to mock imported named function in Jest when module is unmocked

This answer in particular explains that in order to get this approach to work when you're using a transpiler like Babel, you might need to refer to exports.functionToMock instead of functionToMock within functionToTest (outside of your tests), like so:

export const functionToTest = () => async (dispatch) => {
    dispatch(exports.functionToMock());
};