1

Despite mocking custom hook, when rendering App component that uses this hook I still get data from call to original api and alsot test gives this error:

  Matcher error: received value must be a mock or spy function

    Received has value: undefined

Why is the hook not mocked?

test('renders table when data is loaded', async () => {
    jest.mock('./hooks/useFetch', () => ({
        __esModule: true,
        default: () => {
            const original = jest.requireActual('./hooks/useFetch');
            return {
                ...original,
                fetchData: jest.fn()
            }
        }
    }));
    const mockFetchData = jest.fn().mockImplementation((data) => {
        global.fetch = jest.fn().mockResolvedValueOnce(data);
    });

    const mockData = [
        { id: 1, title: 'Type 1' },
        { id: 2, title: 'Type 2' },
    ];

    render(<App />);

    await waitFor(() => expect(mockFetchData(mockData)).toHaveBeenCalledTimes(1))
});

Custom hook looks like this:

const useFetch = () => {
    const fetchData = useCallback( async (url, callback) => {
        try {
            const response = await fetch(url)

            const data = await response.json();

            callback(data)
        } catch (error) {
            setError(error.message || 'An error occurred')
        }
    },[]);

    return { fetchData }
};

export default useFetch;

Callback in this hook is a function from App which sets state of data (setData)

I tried implementing answer from here: Can't mock useAsync with react test library but with no success.

bakrall
  • 465
  • 7
  • 21
  • This question is hard to answer without seeing the `useFetch` file as well. Your mocking seems off but can't direct properly without seeing that first. – adsy Jun 16 '23 at 18:14
  • @adsy, you are right, I updated the question – bakrall Jun 17 '23 at 05:51
  • 1
    My advice for this type of testing is to avoid mocking global fetch (or the hook itself, which seems pointless), and instead use Mock Service Workers to mock the endpoint the `fetch` call actually requests. This pattern lets you mock components that actually use the hook as well. https://mswjs.io/ . It's relatively easy to set up and use honestly. – Derek Jun 17 '23 at 05:59
  • 1
    Derek is totally right, but if you still want to go down this road we need to see how `useFetch` is exported also. – adsy Jun 17 '23 at 20:33
  • 1
    @adsy, I updated with info about export. But finally I used msw as Derek suggested and I got tests passing – bakrall Jun 18 '23 at 16:32
  • 1
    @Derek, thanks for suggestion, I used msw and it works fine and I like the library – bakrall Jun 18 '23 at 16:32

1 Answers1

2

Finally, I followed @Derek advice to use https://mswjs.io/. I also got convinced that msw is gonna be best by Kent C. Dodds' blog post: https://kentcdodds.com/blog/stop-mocking-fetch I find the way presented by him simple and useful. Another useful article is this: https://blog.openreplay.com/mocking-api-servers-with-mock-service-worker-msw/

bakrall
  • 465
  • 7
  • 21