【译】如何浅渲染(shallow render) Jest 快照

2019-08-20  本文已影响0人  小黄人get徐先生

如果你的组件正在使用 Jest 快照测试,下面是一些你必须注意的陷阱。这两条你可能会遇到在你编写测试的时候:

  1. 如果实际的快照测试渲染了一个包含很多子组件的组件,那么输出的快照测试将变得太大。这个本身就存在两个问题:A) 仅仅通过查看快照并不能准确的发现快照不同的地方;B) 如果你同时快照测试你的子组件,最终会得到重复的快照输出。
  2. 如果你实际的快照测试组件渲染了大量子组件,则需要在父组件的快照测试中设置子组件的所有 props。因此,你并没有真正将关注点放在父组件上,而是设置子组件所有必要的信息。如果你再分开测试你的子组件,工作将变得重复,因为你必须使用相同的 props 设置来测试他们。最终你会得到重复的测试设置。

如你所见,这两个问题只适用于渲染多个子组件的父组件。因此,如果你可以在快照测试中浅层渲染(shadow render)父组件,从而在测试中只关注父组件;以及不管有没有渲染了子组件都不用担心子组件的输出。

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

import React from 'react';
import renderer form '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 给所有的子组件,那么就会出现第二个问题,因为你必须为所有的子组件设置 props 在你快照测试,即使父组件可能并不关心它们:

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

你希望避免上面两个问题在父组件快照测试的时候,因为这些问题应该在子组件他们自己进行测试。父组件只关心渲染这些子组件。

笔记:浅层渲染(Shallow rendering)快照测试对于你整体的测试战略来说并不是灵丹妙药,你可能会对你的组件工作在集成环境失去信心(例如:父子组件交互)

尽管 React 的测试渲染器提供了 shallow rendering ,但我发现虚拟子组件的渲染输出更适合我的测试用例:

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",
]
`;

与渲染所有子组件来做版本比较相比,它做到了最大程度的简化。此外,你也不需要关心 props 的传递。然而,如果你想测试你的父组件是否传递了必须的 props 给它的子组件,你甚至可以用一个虚拟的子组件来测试它:

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',
    });
  });
});

总而言之,你最终为父组件创建了一个非常轻量级的快照测试,而你将在子组件自身更彻底地进行快照测试。


React 官方文档

测试渲染器 Test Renderer(https://reactjs.org/docs/test-renderer.html

导入

import TestRenderer from 'react-test-renderer'; // ES6
const TestRenderer = require('react-test-renderer'); // ES5 with npm

总览

这个包提供了一个 React 渲染器(React renderer)可以将 React 组件渲染为纯的 JavaScript 对象,而不用依赖于 DOM 或本地移动环境。

本质上,这个包可以使你轻松抓取一个由 React DOM 或 React Native 组件渲染的平台视图层级(类似于 DOM 树)快照,而无需使用浏览器或 jsdom

例子:

import TestRenderer from 'react-test-renderer';

function Link(props) {
  return <a href={props.page}>{props.children}</a>;
}

const testRenderer = TestRenderer.create(
  <Link page="https://www.fackbook.com/">
);

console.log(testRenderer.toJSON());
// { type: 'a',
//   props: { href: 'https://www.facebook.com/' },
//   children: [ 'Facebook' ] }

你可以使用 Jest 的快照测试特性自动化地保存一份 JSON 树的复制到一个文件中,并检查你的测试没有发生变化:这里学习到更多

你也可以遍历输出来找到特殊的节点,并对它们进行断言。

import TestRenderer from 'react-test-renderer';

function MyComponent() {
  return (
    <div>
      <SubComponent foo="bar" />
      <p className="my">Hello</p>
    </div>
  )
}

function SubComponent() {
  return (
    <p className="sub">Sub</p>
  );
}

const testRenderer = TestRenderer.create(<MyComponent />);
const testInstance = testRenderer.root;

expect(testInstance.findByType(SubComponent).props.foo).toBe('bar');
expect(testInstance.findByProps({className: "sub"}).children).toEqual(['Sub']);

参考
TestRenderer.create()
TestRenderer.create(element, options);

使用传递的 React 元素创建一个 TestRenderer 实例。它不使用真正的DOM,但仍然将组件树完全呈现到内存中,因此您可以对其进行断言。返回一个 TestRenderer 实例。

TestRenderer.act()
TestRenderer.act(callback);

类似于 react-dom/test-utils 里面的 act() 方法,TestRenderer.act 为断言准备一个组件。使用这个版本的 act() 就是封装调用 TestRenderer.create and TestRenderer.update。

import {create, act} from 'react-test-renderer';
import App from './app.js'; // The component being tested

// render the component
let root; 
act(() => {
  root = create(<App value={1}/>)
});

// make assertions on root 
expect(root.toJSON()).toMatchSnapshot();

// update with some different props
act(() => {
  root = root.update(<App value={2}/>);
})

// make assertions on root 
expect(root.toJSON()).toMatchSnapshot();
testRenderer.toJSON()
testRenderer.toJSON();

返回一个对象呈现渲染树🌲。这个树仅仅包含平台特定的节点,如 <div> 或 <View> 和他们的 props,但是不包含用户编写的组件。这对于快照测试十分方便。

testRenderer.toTree()

和 toJson() 方法唯一的区别就是它包含用户编写的组件。

......


React 文档

Shallow Renderer

导入

import ShallowRenderer from 'react-test-renderer/shallow'; // ES6
var ShallowRenderer = require('react-test-renderer/shallow'); // ES5 with npm
总览

在编写React的单元测试时,浅层呈现可能会有帮助。浅层呈现允许您呈现一个组件“一层深”,并断言关于其呈现方法返回什么的事实,而不用担心子组件的行为,这些子组件没有实例化或呈现。这并不需要DOM。

例如,如果你拥有如下组件:

function MyComponent() {
  return (
    <div>
      <span className="heading">Title</span>
      <Subcomponent foo="bar" />
    </div>
  );
}

然后你可以断言:

import ShallowRenderer from 'react-test-renderer/shallow';

// in your test:
const renderer = new ShallowRenderer();
renderer.render(<MyComponent />);
const result = renderer.getRenderOutput();

expect(result.type).toBe('div');
expect(result.props.children).toEqual([
  <span className="heading">Title</span>,
  <Subcomponent foo="bar" />

shallow 测试当前有一些限制,例如不支持 refs。

提示:我们建议你使用 Enzyme 的 Shallow Rendering API 它在相同的功能上提供了更高级的 API


参考
shallowRenderer.render()

您可以将shallowRenderer视为呈现正在测试的组件的“位置”,并从中提取组件的输出。

render()类似于ReactDOM.render(),但它不需要DOM,只呈现一个层次的深度。这意味着您可以测试与子组件的实现方式隔离的组件。

shallowRenderer.getRenderOutput()

调用了shallowRenderer.render()之后,您可以使用shallowRenderer.getRenderOutput()获得浅呈现的输出。

然后可以开始断言关于输出的事实。

上一篇 下一篇

猜你喜欢

热点阅读