Search code examples
reactjsreduxjestjsenzymeaws-amplify

Why does the 'then' part in async test fail in jest?


Component:

export const fetchList = () => {
  return API.get(AMPLIFY_ENPOINTS.default, API_URLS.list, { response: true });
}

const List: React.FC = () => {
  const dispatch = useDispatch();
  const setError = useError();

  useEffect(() => {
    fetchList()
      .then((response) => {
        if (response && response.data?.length) {
          dispatch(setList(response.data));
        }
      })
      .catch((error) => {
        setError(error);
      });
  }, [])
}

Test:

it('should fetch list', async () => {
  const wrapper = mount(
    <Provider store={store}>
      <List />
    </Provider>
  );
  API.get = jest.fn().mockImplementation(() => Promise.resolve({ data: mockList }));
  const response = await fetchList();
  console.log(store.getActions(), response); // HERE IS THE ISSUE
});

So the store.getActions() returns setError from catch block, why is that? It should return setList from then block. What am I doing wrong? response variable returns mockList just fine.

Edit The error it returns is API not configured, I'm using aws amplify.


Solution

  • fetchList is called when the component is mounted, mocked API.get doesn't affect the first time it's called, and second call doesn't do anything. It's a bad practice to mock methods by assigning a spy to them because they cannot be restored after a test.

    The problem with fetchList is that it cannot be spied or mocked because it's used in the same module it's defined. The promise it creates in useEffect cannot be chained, promises need to be flushed in order to avoid race condition.

    It can be:

      let flushPromises = () => new Promise(resolve => setImmediate(resolve));
    
      jest.spyOn(API, 'get').mockResolvedValue({ data: mockList });
    
      const wrapper = mount(
        <Provider store={store}>
          <List />
        </Provider>
      );
    
      await flushPromises();
    
      expect(store.getActions())...