disgra
disgra

Reputation: 783

How to test redux state update with react testing library and jest

I'm trying to write a test with jest and react testing library to see if the store updates a state and the new state is shown inside the component.

I have a component like:

import { getName } from 'src/store/actions/hello/getName';

const HelloComponent = () => {
    const dispatch = useDispatch();
    const { name, isLoading } = useSelector((state:RootState) => state.Hello)
    useEffect( () => {
       dispatch(getName());
    }, []);
    return (
        <div>
             { name &&
               Hello {name}
             }
        </div>
    )
}

There is the store which calls an API like:

const getName = () => (dispatch) => {
  const URL = getUrl()
  fetch(URL, {method: 'GET'})
    .then((response) => response.json())
    .then(({ data }) => dispatch({
      type: 'SAVE_NAME',
      payload: data.name
    })) # This action updates the name inside the redux state
};

I'm using mswjs to mock the API call and I'd like to test that after the component mount, the DOM shows 'Hello John'.

This is the test I've written, but it doesn't work:

it('shows the name', async () => {
   const {findByText} = renderWithStore(<HelloComponent />);
   expect(await findByText('Hello John')).toBeInTheDocument();
});

renderWithStore mocks the store.

import configureStore from 'redux-mock-store';
import { render as rtlRender } from '@testing-library/react'
import { initialState as Hello } from '../src/store/reducers/helloReducer';


const mockStore = configureStore()

const initialStateMock = {
   Hello
}
function render(
  ui
) {
  const store = mockStore(initialStateMock);
  function Wrapper({ children }) {
    return <Provider store={store}>{children}</Provider>
  }
  return rtlRender(ui, { wrapper: Wrapper })
}

It seems like it doesn't wait for the state to update.

any help is much appreciated

Thanks

Upvotes: 11

Views: 39422

Answers (1)

disgra
disgra

Reputation: 783

I think I have found the problem.

The redux-mock-store library doesn't allow to test the state change. The component inside is changing the "real" store state and not the mocked one but it is using the mocked store state when rendering which doesn't change.

In this test I don't need to pass an initial store different from the original and I can use the "real" store without mocking it:

 import {render} from '@testing-library/react'
 import store from 'path_to_the_app_store_obj';
 
 it('shows the name', async () => {
   const {findByText} = render(
        <Provider store={store}>
            <HelloComponent />
        </Provider>
    );
   expect(await findByText('Hello John')).toBeInTheDocument();
 });

Using the original store the test works.

In addition, sometimes you may want to wait for the store to change, in these cases, I found it useful to add:

 await act(() => sleep(500));

after fired the store action and before the "expect".

Upvotes: 10

Related Questions