Home > OS >  Unit testing useLoaderData() react-router v6 loader functions
Unit testing useLoaderData() react-router v6 loader functions

Time:02-02

A project uses react-router v6 and in some components I call useLoaderData(). For instance:

const routes = [
    { path: "widgets", 
      loader: () => fetchAndGetJSON("/api/widgets"), 
      element: <ListWidgets/> }
];

function ListWidgets() {
    const widgets = useLoaderData();
    return <>
        <p>Here is a list of {widgets.length} widgets:
        <...>
    </>;
}

When testing I do not want to execute the fetch, I want to supply the list of widgets in the test.

How can I create a unit test for ListWidgets that uses data that I supply in the test?

CodePudding user response:

I haven't used React Router's data APIs myself, but as I understand it, there are three main alternatives.

  1. Use Jest's standard mocking functionality to mock fetch, mock fetchAndGetJSON, or similar.
    global.fetch = jest.fn(() =>
      Promise.resolve({
        json: () => Promise.resolve(FAKE_TEST_DATA),
      })
    );
    
    beforeEach(() => fetch.mockClear());
    
  2. Use createMemoryRouter with a testing version of your route that renders <ListWidgets/> with a custom loader that returns the test data that you want.
    test(ListWidgets, async () => {
      const routes = [{
        path: "widgets",
        element: <ListWidgets />,
        loader: () => FAKE_TEST_DATA,
      }];
    
      const router = createMemoryRouter(routes, { initialEntries: ["widgets"] });
    
      render(<RouterProvider router={router} />);
      // ...testing...
    }
    
  3. Use Mock Service Worker (msw.js) to create a mock back-end. Functionally, this is pretty similar to mocking fetch yourself, but MSW is very good at streamlining and consolidating things.
    const worker = setupWorker(
      rest.get('/api/widgets', async (req, res, ctx) => {
        const { username } = await req.json();
        return res(ctx.json(FAKE_TEST_DATA))
      }),
    );
    
    worker.start();
    

I'm a big fan of msw.js, but any of the options should work. (It's something of a trade-off: overriding loader results in more narrowly target unit tests, while msw.js lets you write less invasive tests closer to the integration testing end of the spectrum.)

  • Related