how can I test async actions that changes the UI on my React Native code

Viewed 543

im using jest with @testing-library/react-native

code:

a button click dispatches a function:

  onPress(_, dispatch) {
      dispatch(getUserAddresses());
    },

this function is async and is a thunk middleware:

export const getUserAddresses = () => async (dispatch) => {
  const res = await retrieveUserAddresses();

  if (res.status === 'success') {
    dispatch(setLocation(res.data));
  }
}; 

it triggers an axios action:

export const retrieveUserAddresses = async () => {
  const token = await getToken();

  try {
    const res = await instance({
      method: 'GET',
      url: '/addresses',
      headers: { Authorization: `Bearer ${token}` },
    });

    return res.data;
  } catch (error) {
    return error.response;
  }
};

if response is OK, this will trigger this function:

export const setLocation = (data) => ({
  type: constants.SET_LOCATION,
  data,
});

which will in the end change a state on my reducer from false to true this will make a component mount or unmount.

when it comes to testing, when I run this code:

// ...
it('...', () => {
  // ...
    const { getAllByTestId, getByTestId } = render(
      <MockedNavigator component={User} name={USER_ROUTE} />,
    );
     
    // this fire event should click the button that dispatches getUserAddresses();
    fireEvent.press(getAllByTestId('User-option')[1]);
  // ...

});

because the getUserAddresses() function is asynchronous when I check if my component prop is "true", it says it's false. but if I for instance, remove the async stuff from this function, it says it's true because indeed I clicked the button that dispatches the getUserAddresses() function.

I already tried to use MSW but its not working properly on react native for me. (although it works on reactjs)

how can I test it?

2 Answers

You need to use waitFor to wait you UI async changes after click the button

Example:

it('...', async () => { // add async to your test
  // ...
    const { getAllByTestId, getByTestId } = render(
      <MockedNavigator component={User} name={USER_ROUTE} />,
    );
     
    // this fire event should click the button that dispatches getUserAddresses();
    fireEvent.press(getAllByTestId('User-option')[1]);

    // add waitFor with await in action that you expect
    await waitFor(() => {
      // use your expect here
    })

  // ...

});

you can see a full example here https://github.com/akinncar/react-native-mask-text/blob/main/src/components/MaskedTextInput.test.tsx#L12

Two ways come to mind how to approach it. First one waitFor where you got example from first comment

await waitFor(() => expect(mockAPI).toHaveBeenCalledTimes(1))

Second one findBy

await screen.findByText('[SOME-TEXT-FROM-USER-COMPONENT]')

or some other query with that is findBy.

I think findBy should be work, as you want to see something from the User component, so you just search something from there.

Edit:
Make sure you did not mock anything by accident. Happened to me and others that the test is good but because of mock it fails

Related