在 Enzyme 中测试延迟加载的组件

ale*_*17k 1 javascript reactjs jestjs enzyme

给定一个包含多个延迟加载路由的简单应用程序,

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>
  );
}
Run Code Online (Sandbox Code Playgroud)

如何进行测试来检查这些组件是否正在该特定路线上呈现?

这是我尝试过的 App.test:

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);
  });
});
Run Code Online (Sandbox Code Playgroud)

由于悬念,所有 3 个断言似乎都不起作用;可以在此处的codesandbox 找到工作片段- 确保进入“测试”选项卡以查看失败的测试。

非常感谢任何建议,提前谢谢您!

tmh*_*005 7

这是一个有趣的问题,很难有最好的方法来模拟,因为它lazy(() => import('path/to/file'))接受一个函数作为参数,所以我们无法检测匿名函数的值。

但我想我有一个适合您的解决方案,但最好不要测试所有情况,而是测试特定情况才有效。你会嘲笑如下:


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
  };
});
Run Code Online (Sandbox Code Playgroud)

lazy更新了mock函数的新方式

我认为我有一个更好的主意来调用lazy参数然后作为组件返回,如下所示:

jest.mock('react', () => {
  const React = jest.requireActual('react');
  const Suspense = ({ children }) => {
    return children;
  };
  
  const lazy = jest.fn().mockImplementation((fn) => {
    const Component = (props) => {
      const [C, setC] = React.useState();

      React.useEffect(() => {
        fn().then(v => {
          setC(v)
        });
      }, []);

      return C ? <C.default {...props} /> : null;
    }

    return Component;
  })

  return {
    ...React,
    lazy,
    Suspense
  };
});
Run Code Online (Sandbox Code Playgroud)

然后,您必须等待模拟中返回的组件更新lazy,因此我们等待组件重新绘制,如下所示:

// keep warning `act` removed
import { act } from 'react-dom/test-utils';

// A helper to update wrapper
const waitForComponentToPaint = async (wrapper) => {
  await act(async () => {
    await new Promise(resolve => setTimeout(resolve));
    wrapper.update();
  });
};

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

  await waitForComponentToPaint(wrapper);

  expect(wrapper.exists(PageNotFound)).toEqual(true);
});

it("renders Component", async () => {    
  const wrapper = mount(
    <MemoryRouter initialEntries={["/component"]}>
      <App />
    </MemoryRouter>
  );

  await waitForComponentToPaint(wrapper);

  expect(wrapper.exists(Component)).toEqual(true);
});

Run Code Online (Sandbox Code Playgroud)

链接的另一个更新

我创建了一个repl.it链接供您检查其工作原理: https: //repl.it/@tmhao2005/js-cra

您可以运行测试:yarn test -- lazy。并浏览 下的代码src/Lazy