如何对Next.js动态组件进行单元测试?

How to unit test Next.js dynamic components?

Next.js dynamic() HOC 组件并不是很容易测试。我现在有 2 个问题;

如果您遇到 Next8 的第一个问题,您可以使用以下方法模拟动态导入:

jest.mock('next-server/dynamic', () => () => 'Dynamic');

参考:

https://spectrum.chat/next-js/general/with-jest-and-dynamic-imports-broken~25905aad-901e-41d8-ab3e-9f97eeb51610?m=MTU1MzA5MTU2NjI0Nw==

https://github.com/zeit/next.js/issues/6187#issuecomment-467134205

假设我们有这样一个组件(使用动态导入):

import dynamic from 'next/dynamic';

const ReactSelectNoSSR = dynamic(() => import('../components/select'), {
    loading: () => <Input />,
    ssr: false
});

export default () => (
    <>
        <Header />
        <ReactSelectNoSSR />
        <Footer />
    </>
);

Next.js 提供的动态导入支持没有 提供在 Jest 环境中预加载动态导入组件的方法。 但是,感谢 jest-next-dynamic,我们可以渲染完整的组件树而不是加载占位符。

您需要像这样将 babel-plugin-dynamic-import-node 添加到您的 .babelrc

{
  "plugins": ["babel-plugin-dynamic-import-node"]
}

然后,您可以使用 preloadAll() 来渲染组件而不是加载占位符。

import preloadAll from 'jest-next-dynamic';
import ReactSelect from './select';

beforeAll(async () => {
    await preloadAll();
});

Source

您可以将以下内容添加到您的 Jest 设置中:setupTests.ts

jest.mock('next/dynamic', () => () => {
    const DynamicComponent = () => null;
    DynamicComponent.displayName = 'LoadableComponent';
    DynamicComponent.preload = jest.fn();
    return DynamicComponent;
});

虽然这是一个 hacky 解决方案,但我所做的只是通过提取导入路径并返回该导入来简单地模拟 next/dynamic

jest.mock('next/dynamic', () => ({
  __esModule: true,
  default: (...props) => {
    const matchedPath = /(.)*(\'(.*)\')(.)*/.exec(props[0].toString());
    if (matchedPath) return require(matchedPath[3]);
    else return () => <></>;
  },
}));

以下将加载所需的组件。 您也可以使用类似的方法预先加载所有组件。

jest.mock('next/dynamic', () => ({
  __esModule: true,
  default: (...props) => {
    const dynamicModule = jest.requireActual('next/dynamic');
    const dynamicActualComp = dynamicModule.default;
    const RequiredComponent = dynamicActualComp(props[0]);
    RequiredComponent.preload
      ? RequiredComponent.preload()
      : RequiredComponent.render.preload();
    return RequiredComponent;
  },
}));