Search code examples
reactjsunit-testingjestjsenzymedry

Follow DRY principles while writing jest mocks in test cases - React and Jest


I'm new to testing and I wanted to follow the DRY principle for my unit test cases. In my test cases, I'm mocking react-redux's useSelector and useDispatch and currently writing the same mock in all the tests wherever react-redux is used. Below is one of my test case.

import React from 'react';
import { shallow } from 'enzyme';
import LayoutComponent from './LayoutComponent';


const mockDispatch = jest.fn();
jest.mock('react-redux', () => ({
    ...jest.requireActual('react-redux'),
    useSelector: jest.fn(),
    useDispatch: () => mockDispatch
}));

const setup = () => {
    return shallow(<LayoutComponent />)
}

const mockState = (state) => {
    return useSelector.mockImplementationOnce(callback => {
        return callback(state);
    })
}

jest.mock("react-router-dom", () => ({
    ...jest.requireActual("react-router-dom"),
    useLocation: () => ({
        pathname: "localhost:3000/"
    })
}));

describe('LayoutComponent', () => {
    afterEach(() => {
        jest.clearAllMocks();
    })
    test('Calls getUserInfo on mount', () => {
        setup();
        expect(mockDispatch).toHaveBeenCalledWith(expect.any(Function));
expect(mockDispatch).toHaveBeenCalledTimes(1);
    });

});

Instead of writing this mock in all the test files, I want to keep react-redux mock separate so that I can import it in all the files wherever these are used and no need to write them again and again.

Can anyone please help me with this. Thank you in advance.

UPDATE

I have created a directory called __mocks__ right beside node_modules folder and inside __mocks__ folder I've created react-redux.js file. Inside the file I've the below mock functions.

const mockDispatch = jest.fn()

module.exports = {
    ...jest.requireActual('react-redux'),
    __esModule: true,
    useSelector: jest.fn(),
    useDispatch: () => mockDispatch,
    mockDispath,
};

Below is the test

import React from 'react';
import { shallow } from 'enzyme';
import ResolutionCount from '../ResolutionCount';
import { mockDispatch, mockState } from 'react-redux';

const setup = props => {
    return shallow(<ResolutionCount componentUsedIn={props} />)
}

describe("ResolutionCount component", () => {
    test("testing component with prop value", () => {
        mockState({
            userInfoReducer: {
                userInfo: [
                    {
                        user_id: 'some id',
                        user_name: 'some name',
                        user_email: 'someemail@email.com',
                        user_pic: null,
                        is_admin: true,
                        is_super_user: false,
                    },
                ]
            }
        });

        setup("my resolutions");

        expect(mockDispatch).toHaveBeenCalledWith({
            type: "SET_RESOLUTION_STATUS_COUNT_BAR_CHART_FETCHING_DATA",
            payload: true,
        });

    })
})

so now when I'm running the test I'm getting the error saying that

TypeError: (0 , _reactRedux.mockState) is not a function
 describe("ResolutionCount component", () => {
      11 |     test("testing component with prop value", () => {
    > 12 |         mockState({
         |         ^
      13 |             userInfoReducer: {
      14 |                 userInfo: [
      15 |                     {

Error Folder structure


Solution

  • Reusable mocks from __mocks__ directory is a way that Jest provides for this.

    In case a spy needs to be accessed directly in order to modify the implementation or assert it, it can be added to module exports, name collisions with existing exports have to be avoided with mock prefix or else.

    <root>/__mocks__/react-redux.js

    const mockDispatch = jest.fn();
    
    module.exports = {
        ...jest.requireActual('react-redux'),
        __esModule: true,
        useSelector: jest.fn(),
        useDispatch: jest.fn(() => mockDispatch),
        mockDispatch
    };
    

    The mock is supposed to be automatically applied on import. Then mockDispatch can be imported in tests as usual:

    import { mockDispatch } from 'react-redux';