Skip to content

Latest commit

History

History
115 lines (85 loc) 路 2.85 KB

Dispatches.md

File metadata and controls

115 lines (85 loc) 路 2.85 KB

Dispatches

Typical Setups

React function components may dispatch Redux actions using react-redux's useDispatch hook. Consider this Redux state shape containing a clicked of type boolean:

const initialState = {
  interactions: {
    clicked: false,
  },
};

Your state code might declare a markClicked action:

const MARK_CLICKED = "MARK_CLICKED";

const markClicked = () => ({ type: MARK_CLICKED });

...and some kind of reducer to set clicked to true upon the markClicked action:

switch (action.type) {
  case MARK_CLICKED:
    return {
      ...state,
      interactions: {
        ...state.interactions,
        clicked: true,
      },
    };
}

Your view code might dispatch an action to set clicked to true:

const Clicker = () => {
  const dispatch = useDispatch();

  return (
    <button onClick={() => dispatch(markClicked())} type="button">
      Click me!
    </button>
  );
};

View Testing

You could set up Redux state with a full state tree:

const store = configureStore()({
  interactions: {
    clicked: false,
  },
});

const view = render(
  <Provider store={store}>
    <Clicker />
  </Provider>,
);

act(() => {
  fireEvent.click(view.getByRole("button"));
});

expect(store.actions()).toEqual([markClicked()]);

So much work! That testing setup's complexity and resultant pain can easily grow with your application. You're also relying a bunch of Redux state in order to test your React view. It can be useful to separate view and state logic in tests: especially when state is shared across many places.

Instead, mock-react-redux can completely replace any Redux interactions. It'll instead swap out the dispatch function with a Jest mock:

const { dispatch } = mockReactRedux();

const view = render(<Clicker />);

act(() => {
  fireEvent.click(view.getByRole("button"));
});

expect(dispatch).toHaveBeenCalledWith(markClicked());

Much cleaner!

dispatch

The dispatch returned to your views is directly the result of calling jest.fn(). It'll receive any actions passed to it as normal function parameters.

If you'd like to execute some logic when actions are dispatched, you can use Jest's built-in logic for mock implementations:

dispatch.mockImplementation((action) => {
  console.log("Received", action);
});

Examples

  • Clicker: Tests for the Clicker component above
  • Tracking: Component tests that exercise both dispatch and selector logic