Mock useDispatch in jest

Posted by

This article addresses how to mock useDispatch in component test code. Moreover, it suggests a solution to the situation where action ends up being an anonymous function.

[Function (anonymous)]

How to test a component with useDispatch

Let’s say you want to test a component where an action will be dispatched by click event.

mock setup

jest.mock('react-redux');
const useDispatchMock = useDispatch as jest.Mock;
const mockNotificationClear = jest.fn();

useDispatchMock.mockImplementation(() => {
  return jest.fn().mockImplementation((action) => {
    switch (action.type) {
      case 'notification/clear':
          mockNotificationClear();
          break;
      default:
          break;
    }
  });
});

test

it('notification clears', () => {
    renderComponent(); // render a component to test
    act(() => {
        fireEvent.click(screen.getByTestId('notification-clear'));
    });
    expect(mockNotificationClear).toBeCalled();
});

How to handle action that references ‘anonymous function’

problem

I encountered a situation where action.type is undefined when dispatch is called. So, I used console.log to inspect what action references.

[Function (anonymous)]

Because of this, I cannot identify which action has been called!

solution

mock setup

jest.mock('react-redux');
const useDispatchMock = useDispatch as jest.Mock;
const mockNotificationClear = jest.fn();

const mockDispatch = jest.fn(); // default to this if action.type cannot be identified

useDispatchMock.mockImplementation(() => {
  return jest.fn().mockImplementation((action) => {
    switch (action.type) {
      case 'notification/clear':
          mockNotificationClear();
          break;
      default:
          mockDispatch(action(useDispatchMock).arg);
          break;
    }
  });
});
  1. Test if dispatch is called even though we wouldn’t know which action is called.
  2. Pass the arguments that has been passed to the anonymous action so that we can test if the correct arguments are passed. action().arg allows access to what arguments has been passed.
  3. Pass useDispatchMock to action(). Unless so, if there is another dispatch to run, it wouldn’t be called.

TypeError: dispatch is not a function

test

it('updates a post', () => {
    renderComponent(); // render a component to test

    const postTitle = screen.getByTestId('post-title');
    const postBody = screen.getByTestId('post-body');
    fireEvent.change(postTitle, { target: { value: 'test' }});
    fireEvent.change(postBody, { target: { value: 'test' }});

    act(() => {
        fireEvent.click(screen.getByTestId('update-post'));
    });
    expect(mockDispatch).toBeCalledWith({ title: 'test', body: 'test' });
});

Thanks for reading.

Hope you enjoyed the article. If you have any question or opinion to share, feel free to write some comments.

Facebook Comments