Mocking React Components with Jest

Edward Loveall

I recently had a troublesome React component. It was a component that was using the browser’s fetch API to load and insert SVG icons the page. In and of itself that’s fine. Browsers have all the required components to use fetch. However, Node.js is not a browser and it doesn’t have fetch. So what can we do?

One thing we could do is use some fetch-like polyfill. There’s node-fetch, fetch-mock, jest-fetch-mock, cross-fetch, and many others that might help us do that. However, this not only involves modifying the global object to add fetch, but also mocking every call to fetch so it returns the icons that we want. Since most of the time I’m not testing the icons of a component, it would be inappropriate to mock this for unit tests.

The way I solved this was to use Jest mocks and mocked out the whole icon component, so that it never calls fetch. We stopped making fetch happen, but the solution wasn’t intuitive.

Mock ES Modules with Jest

First, to mock a component, you use jest.mock("path/to/RealComponent"). You can specify an mock implementation inline like jest.mock("../src/Icon" () => { ... }).

It needs to return a module, which is an object with keys as the exports. So if your component normally exports like so:

export { A, B };

Then your Jest mock can supply both of those exported things:

jest.mock("../src/Icon", () => {
  return {
    A: true,
    B: () => {
      return <></>;
    },
  };
});

Mock ES Modules with default and named exports

What about default exports, or both named and default exports? To mock a module with both, you can use the default key, but you also must specify that it’s an ES6 module with a special __esModule key:

export { A };
export default B;
jest.mock("../src/Icon", () => {
  return {
    __esModule: true,
    A: true,
    default: () => {
      return <div></div>;
    },
  };
});

You can then put this in your test, but it must be outside of any scope to work. It can’t be in a function, a before block, or anything else. This is a pain to abstract and use in other (all) tests. So I used a mock file.

Mock ES Modules with Jest file mocks

These mocks need to be in a __mocks__ folder next to the component, and also have the same file name. For example, your folder structure might look like:

src
├── Icon.tsx
├── __mocks__
│   └── Icon.tsx

and then you can reimplement the component:

// src/__mocks__/Icon.tsx

import React from "react";
import { ReactElement } from "react";

const A = "abcdefg";
const B = (): ReactElement => {
  return <></>;
};

export { A };
export default B;

Finally, in your test, you can mock the real component, and Jest will magically find the mock next door:

jest.mock("../src/Icon");

If you want all tests to benefit from this file, put this in your Jest setup file, or just pick on a test-by-test basis. Either way, mocking is a powerful tool in your toolset, and now you can mock components.