reactjsreact-reduxjestjsreact-testing-libraryredux-mock-store

Testing redux connected components with jest and redux-mock-store


I want to test connected React component. I am using Jest, React Testing Library and redux-mock-store. I'm mocking the getPatternPageInfo function and When I'm trying to get some elements, I get an error:

TypeError: SidebarActions_1.selectPattern is not a function

package.json

"redux-mock-store": "^1.5.4",
"typescript": "^3.9.7",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.0.4",
"@types/jest": "^26.0.13",
"@types/redux-mock-store": "^1.0.2",
"jest": "^26.4.2",
"ts-jest": "^26.3.0",
"react-redux": "5.0.7",
"react-router-dom": "^5.1.2",
"redux": "3.7.2",

SidebarActions.ts

import axios from "axios";
import { ActionType, AppDispatch } from "./Actions";
import { Pattern } from "../reducers/SidebarMenuReducer";
import { simpleDataExtract } from "./common/common";
import { ResponseStatus } from "../../enums/status";
import { Urls } from "../../enums/Urls";
import { SelectedItemTypes } from "../../enums/SelectedItemTypes";

export const getPatternPageInfo = (patternId: number): Promise<Pattern> => {
    return axios
        .get(`${Urls.GET_PATTERN_PAGE_INFO}?patternId=${patternId}`)
        .then(simpleDataExtract)
        .then(response => {
            if (response.status === ResponseStatus.OK) return response.data;
        });
};

export const selectPattern = (patternId: number | undefined) => (dispatch: AppDispatch) => {
    dispatch({
        type: ActionType.SET_SELECTED_ITEM,
        data: { id: patternId, type: SelectedItemTypes.PATTERN },
    });
};


PatternForm.tsx

import * as React from "react";
import { FC, useEffect, useState } from "react";
import { Box, Grid, } from "@material-ui/core";
import { connect } from "react-redux";
import { Pattern } from "../redux/reducers/SidebarMenuReducer";
import { AppDispatch } from "../redux/actions/Actions";
import { getPatternPageInfo, selectPattern } from "../redux/actions/SidebarActions";
import PageLoader from "../components/loaders/PageLoader";

interface Events {
    onPatternClick(patternId: number): void;
}

interface MatchParams {
    params: { patternId: number | undefined };
    isExact: boolean;
    path: string;
    url: string;
}

interface Match {
    match: MatchParams;
}

const PatternForm: FC<Events & Match> = props => {
    const [pattern, setPattern] = useState<Pattern | undefined>(undefined);
    const [isLoading, setIsLoading] = useState<boolean>(true);

    const { onPatternClick } = props as Events;
    const { match } = props as Match;

    const patternId = match && match.params && Number(match.params.patternId);

    useEffect(() => {
        if (patternId) {
            setIsLoading(true);
            getPatternPageInfo(patternId).then(data => {
                setPattern(data);
                setIsLoading(false);
            });
        }
        onPatternClick(patternId);
    }, [patternId]);

    if (isLoading) return <PageLoader />;

    return (
        <Box data-testid="content" className="layout__content">
            <Grid item xs className="layout__body">
               Test
            </Grid>
        </Box>
    );
};

const mapDispatchToProps = (dispatch: AppDispatch): Events => ({
    onPatternClick: patternId => dispatch(selectPattern(patternId)),
});

export default React.memo(connect(null, mapDispatchToProps)(PatternForm));

PatternForm.test.tsx

import React from "react";
import { render, fireEvent, screen } from "@testing-library/react";
import { Provider } from "react-redux";
import configureMockStore from "redux-mock-store";
import thunk from "redux-thunk";
import PatternForm from "../../pages/PatternForm";
import { Pattern, Oiv } from "../../redux/reducers/SidebarMenuReducer";

const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
const store = mockStore({});

jest.mock("../../redux/actions/SidebarActions", () => ({
    getPatternPageInfo: jest.fn(),
}));

const getPatternPageInfo = require("../../redux/actions/SidebarActions").getPatternPageInfo as jest.Mock;

const match = {
    params: { patternId: 1 },
    isExact: true,
    path: "path",
    url: "url",
};

describe("<PatternForm/>", () => {
    it("should render data", () => {
        const pattern = {
            id: 936,
            name: "test",
            oivs: [{ id: 29109, name: "oiv" } as Oiv],
        } as Pattern;
        getPatternPageInfo.mockImplementationOnce(() => Promise.resolve(pattern));

        render(
            <Provider store={store}>
                <PatternForm match={match} />
            </Provider>,
        );
        expect(screen.getByTestId("content")).toBeInTheDocument();
    });
});

Solution

  • The solution in this docs worked for me https://jestjs.io/docs/en/jest-object#jestrequireactualmodulename

    PatternForm.test.tsx

    jest.mock("../../redux/actions/SidebarActions", () => {
        const originalModule = jest.requireActual("../../redux/actions/SidebarActions");
    
        return {
            ...originalModule,
            getPatternPageInfo: jest.fn(),
        };
    });
    
    const getPatternPageInfo = require("../../redux/actions/SidebarActions").getPatternPageInfo as jest.Mock;