如何使用 Jest 和新的 React lazy 16.6 测试快照 API

How to test snapshots with Jest and new React lazy 16.6 API

我必须使用新的 React lazy API (16.6) 导入组件。

import React, {PureComponent, lazy} from 'react';

const Component1 = lazy(() => import('./Component1'));
const Component2 = lazy(() => import('./Component2'));

class CustomComponent extends PureComponent {
  ...
  render() {

  return (
    <div>
      <Component1 />
      <Component2 />
    </div>
  );
 }
}

在我的测试中,我正在做这个组件的快照。这是一个非常简单的测试:

import { create } from 'react-test-renderer';

const tree = await create(<CustomComponent />).toJSON();

expect(tree).toMatchSnapshot();

在日志中,测试失败并出现以下错误:

A React component suspended while rendering, but no fallback UI was specified.

Add a <Suspense fallback=...> component higher in the tree to provide a loading indicator or placeholder to display.

我是否必须用 <Suspense>... 包装每个测试套件?

it('should show the component', async () => {
  const component = await create(
    <React.Suspense fallback={<div>loading</div>}>
     <CustomComponent /> 
    </React.Suspense> 
  ); 
  const tree = component.toJSON(); 

  expect(tree).toMatchSnapshot(); 

};

如果我这样做,我只会在快照中看到 fallback 组件。

+ Array [ + <div> + loading + </div>, + ]

那么,最好的方法是什么?

我是否必须用 <Suspense> 包装每个测试套件?

Yes, the Suspense component is neccessary for lazily loading child components, particularly providing a fallback and for reconciliation when the lazy components are available.

CustomComponent 中导出 Component1Component2,以便可以在测试中导入它们。

import React, {PureComponent, lazy} from 'react';

export const Component1 = lazy(() => import('./Component1'));
export const Component2 = lazy(() => import('./Component2'));

export default class CustomComponent extends PureComponent {
  //...
}

请记住,延迟加载的组件是类似 promise 的。 在测试中导入它们,并在检查快照是否匹配之前等待它们解析。

import { create } from 'react-test-renderer';
import React, {Suspense} from 'react';
import CustomComponent, {Component1, Component2} from './LazyComponent';

describe('CustomComponent', () => {
  it('rendered lazily', async()=> {
    const root = create(
      <Suspense fallback={<div>loading...</div>}>
        <CustomComponent/>
      </Suspense>
    );

    await Component1;
    await Component2;
    expect(root).toMatchSnapshot();
  })
})

我有一个类似的问题,我想对嵌套组件和其中一个延迟加载的组件进行快照测试。嵌套看起来像这样:

SalesContainer -> SalesAreaCard -> SalesCard -> AreaMap

其中 SalesContainer 是顶级组件。 AreaMap-组件由 SalesCard 使用 React lazy 和 Suspense 延迟加载。大多数开发人员在快照中呈现 AreaMap 本地通过的测试。但是在 Jenkins CI 中,测试总是惨败,AreaMap 从未呈现。至少可以说是片状的。

为了使测试通过,我在测试中添加了神奇的行 await testRenderer.getInstance().loadingPromise;。这是一个测试的例子:

import React from 'react';
import renderer from 'react-test-renderer';
import wait from 'waait';
import SalesContainer from './index';

describe('<SalesContainer />', () => {
it('should render correctly', async () => {
    const testRenderer = renderer.create(
      <SalesContainer />
    );
    await wait(0);
    await testRenderer.getInstance().loadingPromise;
    expect(testRenderer).toMatchSnapshot();
  });
});

根据此 comment in github,您可以使用 Jest 将惰性组件模拟为 return 实际组件,尽管您需要将惰性语句移动并导出到它们自己的文件中才能使用工作。

// LazyComponent1.ts
import { lazy } from 'react';

export default lazy(() => import('./Component1'));
// CustomComponent.tsx
import React, { PureComponent } from 'react';
import Component1 from './LazyComponent1';
import Component2 from './LazyComponent2';

class CustomComponent extends PureComponent {
  ...
  render() {

  return (
    <div>
      <Component1 />
      <Component2 />
    </div>
  );
 }
}
// CustomComponent.spec.tsx
import React, { Suspense } from 'react';
import { create } from 'react-test-renderer';
import CustomComponent from './CustomComponent';

jest.mock('./LazyComponent1', () => require('./Component1'));
jest.mock('./LazyComponent2', () => require('./Component2'));

describe('CustomComponent', () => {
  it('should show the component', () => {
    const component = await create(
      <Suspense fallback={<div>loading</div>}>
       <CustomComponent /> 
      </Suspense> 
    ); 
    const tree = component.toJSON(); 

    expect(tree).toMatchSnapshot(); 
  });
});

使用 Enzyme 和 mount 这对我有用。它不需要更改任何导出。

// wait for lazy components
await import('./Component1')
await import('./Component2')

jest.runOnlyPendingTimers()
wrapper.update()

感谢 接受的答案。

在某种程度上,这两个答案的组合是唯一对我有用的东西(使用酶安装)

我不得不使用 and the await logic of

的导出逻辑
//use at own risk
await (Component1 as any)._ctor();
wrapper.update()

Raine 的回答不知为何在我们的构建代理上不起作用,但在开发环境上起作用