For example, I want to know what has been dispatched and the argument. The action creator is asynchronous, but I don't care about its implementation, I just want to know if the component dispatches the correct action creator with the correct argument. I've tried this approach:
store.dispatch = jest.fn()
But I can't get any useful information:
I've tried to solve the problem by this way:
expect(store.dispatch.mock.calls[0].toString()).toBe(requestArticles().toString())
But I don't know the argument and I'm sure, that there are a better way to do this. Also of note, I'm using react-testing-library, so I can't use wrapper.instance().props
from Enzyme.
For example, I want to know what has been dispatched and the argument. The action creator is asynchronous, but I don't care about its implementation, I just want to know if the component dispatches the correct action creator with the correct argument. I've tried this approach:
store.dispatch = jest.fn()
But I can't get any useful information:
I've tried to solve the problem by this way:
expect(store.dispatch.mock.calls[0].toString()).toBe(requestArticles().toString())
But I don't know the argument and I'm sure, that there are a better way to do this. Also of note, I'm using react-testing-library, so I can't use wrapper.instance().props
from Enzyme.
3 Answers
Reset to default 9Here's an example that worked for me (2022):
(Navbar.test.js)
import store from "../../store/redux-store";
import { useDispatch, useSelector } from "react-redux";
import { Provider } from "react-redux";
import configureStore from "redux-mock-store";
import { render, screen, cleanup, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import Navbar from "../navbar/Navbar";
describe("Navbar", () => {
beforeEach(() => {
// ! WE MAKE SURE THE MOCKS ARE CLEARED BEFORE EACH TEST CASE
useSelectorMock.mockClear();
useDispatchMock.mockClear();
});
afterAll(() => {
cleanup();
});
// ! SETUP THE SPY ON USESELECTOR / USE DISPATCH
// ! WE DO THIS TO BE ABLE TO CHECK IF THE DISPATCH MOCK GOT CALLED AND HOW MANY TIMES
const reactRedux = { useDispatch, useSelector }
const useDispatchMock = jest.spyOn(reactRedux, "useDispatch");
const useSelectorMock = jest.spyOn(reactRedux, "useSelector");
[...]
test("cliking the sign-out button should sign out user", async () => {
const mockStore = configureStore();
const initialState = {
auth: {
isAuthModalOpen: false,
user: { id: 1, email: "[email protected]" },
notification: null,
isLoggedIn: true,
token: "ABC123",
},
};
let updatedStore = mockStore(initialState);
const mockDispatch = jest.fn();
useDispatchMock.mockReturnValue(mockDispatch);
updatedStore.dispatch = mockDispatch;
// ? HERE THE INITIAL CONTENT OF THE MOCK
// console.log(updatedStore.dispatch.mock);
render(<Provider store={updatedStore}><Navbar /></Provider>);
const signOutBtn = screen.getByTestId("button-sign-out");
expect(signOutBtn).toBeInTheDocument();
expect(updatedStore.dispatch).not.toHaveBeenCalled();
userEvent.click(signOutBtn);
// ? HERE THE CONTENT OF THE MOCK CHANGED
// console.log(updatedStore.dispatch.mock);
expect(updatedStore.dispatch).toHaveBeenCalledTimes(1);
expect(updatedStore.dispatch.mock.lastCall[0].type).toMatch("destroySession");
screen.debug()
console.log(updatedStore.getActions());
});
Notice the difference in the way of importing "useDispatch" from react-redux. Also, notice the way "mockDispatch" is attached to "updatedStore.dispatch".
Those 2 changes made it work for me.
You can mock the action file and check if the action has been called.
e.g. Lets say foo.action.js
is the file which has the action being dispatched.
in the start of your test file before importing the component, you can mock the file as:
const yourActionMock = jest.fn();
jest.mock('<Path to the action file>/foo.action.js', () => ({
yourAction: yourActionMock
}));
now you can test the action called as:
expect(yourActionMock).toHaveBeenCalledWith(<args>)
if you're using react-redux hooks you can do it like so:
import * as reactRedux from 'react-redux';
const mockDispatch = jest.fn();
const mockUseDispatch = jest.spyOn(reactRedux, 'useDispatch');
and then make assertions on mockDispatch
as usual.