如何浅层渲染Jest快照测试(附代码)

178 阅读3分钟

如果你使用Jest的快照测试来做你的组件,有几个陷阱是你必须要注意的。其中两个也很可能适用于你的书面测试:

    1. 如果实际的快照测试渲染了一个有很多子组件的组件,那么快照测试的输出往往变得太大。这本身就有两个问题。A) 你不能仅仅通过查看快照输出来确定其差异,B) 如果你对你的子组件也进行快照测试,那么你的快照输出会有点重复。
    1. 如果你的实际快照测试组件渲染了很多子组件,所有子组件的道具都需要在父组件的快照测试中设置。因此,你并没有真正关注父组件,而是在为子组件设置所有必要的信息。如果你在分离测试中再次测试你的子组件,这项任务就会变得重复,因为在那里你必须用同样的道具设置来测试它们。最终,你将会得到重复的测试设置。

正如你所看到的,这两个问题只适用于渲染几个以上的子组件的父组件。那么,如果你能在你的快照测试中浅浅地渲染父组件,只关注测试中的父组件;关注它是否渲染了其子组件的实例,而不担心子组件的整个输出呢?

如果你使用Jest进行快照测试,你很可能是用react-test-renderer渲染你的React组件:

import React from 'react';
import renderer from 'react-test-renderer';

import Profile from '.';

describe('Profile', () => {
  it('renders', () => {
    const component = renderer.create(<Profile />);
    const tree = component.toJSON();
    expect(tree).toMatchSnapshot();
  });
});

如果你在你的Profile组件中渲染大量的子组件,你的快照测试输出可能会出现问题1)。不过你唯一应该关心的是渲染的组件实例,而不是它们的内容。

const Profile = () => (
  <>
    <Preferences />
    <Documents />
    <WorkExperience />
    <Education />
    <Skills />
    <PersonalInfo />
  </>
);

如果Profile组件向其所有的子组件传递大量的props,你就会出现问题2),因为你必须在快照测试中为所有的子组件设置所有的假props,即使父组件可能不关心它们。

const Profile = ({
  ...preferencesProps,
  ...documentsProps,
  ...workExperienceProps,
  ...educationProps,
  ...skillsProps,
  ...personalInfoProps,
}) => (
  <>
    <Preferences {...preferencesProps} />
    <Documents {...documentsProps} />
    <WorkExperience {...workExperienceProps} />
    <Education {...educationProps} />
    <Skills {...skillsProps} />
    <PersonalInfo {...personalInfoProps} />
  </>
);

你想在快照测试中避免1)和2),因为这些问题应该在子组件中测试。父组件可能只关心渲染子组件。

注意:浅层渲染快照测试不是你整体测试策略的银弹。如果你在快照测试中应用浅层渲染,你可能会失去对你的组件在集成中工作的信心(例如,父组件和子组件之间的相互作用)。

即使React的测试渲染器提供了浅层渲染,我发现模拟子组件的渲染输出是更适合我的测试案例的方法。

import React from 'react';
import renderer from 'react-test-renderer';

import Profile from '.';

jest.mock('./Preferences', () => () => 'Preferences');
jest.mock('./Documents', () => () => 'Documents');
jest.mock('./WorkExperience', () => () => 'WorkExperience');
jest.mock('./Education', () => () => 'Education');
jest.mock('./Skills', () => () => 'Skills');
jest.mock('./PersonalInfo', () => () => 'PersonalInfo');

describe('Profile', () => {
  it('renders', () => {
    const component = renderer.create(<Profile />);
    const tree = component.toJSON();
    expect(tree).toMatchSnapshot();
  });
});

你的浅层渲染的快照测试的输出看起来类似于下面:

exports[`Profile renders 1`] = `
Array [  "Preferences",  "Documents",  "WorkExperience",  "Education",  "Skills",  "PersonalInfo",]
`;

与将所有子组件渲染到最大程度的版本相比,这是一种简化的方式。同时,你也不需要再去关心传递的道具。然而,如果你想测试你的父组件是否将所有必要的道具传递给它的子组件,你甚至可以用一个模拟的子组件来测试它。

import React from 'react';
import renderer from 'react-test-renderer';

import Profile from '.';
import PersonalInfo from './PersonalInfo';

jest.mock('./Preferences', () => () => 'Preferences');
jest.mock('./Documents', () => () => 'Documents');
jest.mock('./WorkExperience', () => () => 'WorkExperience');
jest.mock('./Education', () => () => 'Education');
jest.mock('./Skills', () => () => 'Skills');
jest.mock('./PersonalInfo', () => () => 'PersonalInfo');

describe('Profile', () => {
  it('renders and passes props', () => {
    const component = renderer.create(<Profile />);
    const tree = component.toJSON();
    expect(tree).toMatchSnapshot();

    expect(component.root.findByType(PersonalInfo).props).toEqual({
      name: 'Robin Wieruch',
    });
  });
});

总之,你最终会得到一个非常轻量级的父组件的快照测试,而你会用他们的道具更彻底地快照测试你的子组件(如偏好、文档、教育、技能)。