Test lazy loaded components in Enzyme

Given a simple App containing multiple lazy loaded routes,

import React, { lazy, Suspense } from "react";
import { Route } from "react-router-dom";
import "./styles.css";

const Component = lazy(() => import("./Component"));
const PageNotFound = lazy(() => import("./PageNotFound"));

export default function App() {
  return (
    <div className="App">
      <Route
        path="/component"
        exact
        render={() => (
          <Suspense fallback={<div>Loading..</div>}>
            <Component />
          </Suspense>
        )}
      />

      <Route
        path="*"
        render={() => (
          <Suspense fallback={<div>Loading..</div>}>
            <PageNotFound />
          </Suspense>
        )}
      />
    </div>
  );
}

How can tests be made to check if those components are being rendered on that specific route?

Here's the App.test with what I tried:

import { configure, shallow, mount } from "enzyme";
import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
import React from "react";
import { MemoryRouter } from "react-router-dom";
import App from "./App";
import Component from "./Component";
import PageNotFound from "./PageNotFound";

configure({ adapter: new Adapter() });

describe("App", () => {
  it("renders without crashing", () => {
    shallow(<App />);
  });

  it("renders lazy loaded PageNotFound route", () => {
    // Act
    const wrapper = mount(
      <MemoryRouter initialEntries={["/random"]}>
        <App />
      </MemoryRouter>
    );

    // Assert
    // expect(wrapper.containsMatchingElement(<PageNotFound />)).toEqual(true);
    // expect(wrapper.find(PageNotFound)).toHaveLength(1);
    expect(wrapper.exists(PageNotFound)).toEqual(true);
  });
});

All 3 assertions don't seem to be working due to Suspense; A working snippet can be found at codesandbox here - Make sure to go on the 'tests' tab in order to see the failing tests.

Any suggestion is highly appreciated, thank you in advance!

1 answer

  • answered 2021-01-16 15:17 tmhao2005

    This is an interesting question which is hard to have a best way to mock since the lazy(() => import('path/to/file')) takes a function as argument so we can't detect the value of anonymous function.

    But I think I have a solution for you but it's not best to test all cases but a specific case it would work. You would mock as following:

    
    jest.mock('react', () => {
      const React = jest.requireActual('react');
     
      // Always render children as our lazy mock component
      const Suspense = ({ children }) => {
        return children;
      };
    
      const lazy = () => {
        // `require` component directly as we want to see
        // Why? Above reason
        return require('./PageNotFound').default;
      }
    
      return {
        ...React,
        lazy,
        Suspense
      };
    });