原文地址: medium.com/codeclan/te…
译文地址: github.com/xiao-T/note…
本文版权归原作者所有,翻译仅用于学习。
这篇文章将会介绍如何设置并使用 Jest 和 Enzyme 测试通过 Create React App (CRA) 创建的 React 应用。对于那些从头开始的人我们会给出一些建议。但是,不会涉及太多有关 React 的知识。
Jest 和 Enzyme 是两个不同,但又相互相成的工具,整合在一起可以提供更加灵活和更具创造性的测试能力。我们将会简单介绍两者有什么差异。
Jest
Jest 是一个 Javascript 单元测试框架,Facebook 用来测试服务和 React 应用。
CRA 已经内置了 Jest;不再需要单独安装。
Jest 是一个 测试运行器 、 断言库 和 模拟库 。
Jest 也提供
快照
测试,它可以创建一个组件的快照,然后与上一次保存快照对比。如果,前后两者不匹配,测试就会失败。快照将会被保存在名为
__snapshots__
文件夹中,它与被测试的文件在同一目录中。快照看起来就像下面这个样子:
exports[`List shallow renders correctly with no props 1`] = `
<List
ordered={false}
>
<ListItem>
Item 1
</ListItem>
<ListItem>
Item 1
</ListItem>
</List>
`;
复制代码
快照测试必须结合浏览器测试,并且,在一开始就要校验快照,以保证快照能按照期望的输出。
Enzyme
Enzyme 是针对 React 的测试功能库,它可以更容易的断言、操控和遍历 React 组件。
Enzyme,由 Airbnb 创建,并添加了更好的功能方法,比如: 渲染组件 、 查找元素 和 与元素交互 。
在 CRA 中它必须单独安装。
Jest 和 Enzyme
- Jest 和 Enzyme 两者都可以测试 React 应用,Jest 还可以测试其他的 Javascript 应用,但是 Enzyme 只能测试 React。
- Jest 可以单独渲染组件和快照测试,Enzyme 只是添加了更简单的方法。
- 没有 Jest,Enzyme 也可以使用,但是 必须 结合另外的测试运行器(runner)。
综上所述:
- Jest 作为测试运行器,断言库和模拟器使用
- Enzyme 提供额外的测试功能用与交互。
设置
安装和配置
如果不使用 CRA,需要安装 Jest:
npm install --save-dev jest babel-jest
复制代码
安装 Enzyme:
npm install --save-dev enzyme enzyme-adapter-react-16 enzyme-to-json
复制代码
更新
package.json
:
"jest": {
"snapshotSerializers": ["enzyme-to-json/serializer"]
}
复制代码
相比 Enzyme,为了更加方便的对比快照,
enzyme-to-json
提供了更好组件格式化方式。当使用快照时
snapshotSerializers
可以更大限度的压缩重复代码。如果没有指定序列化工具,测试中每次对比快照是否匹配时都需要把组件传递给
enzyme-to-json
的
.toJson()
方法,反之,就不需要。
expect(toJson(rawRenderedComponent)).toMatchSnapshot();
复制代码
通过在
package.json
中添加这一配置,在你调用 Jest 的
.toMatchSnapshot()
时就不需要在调用 JSON 格式化方法了。
在
./src/setupTests.js
创建
setupTets.js
文件:
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';configure({ adapter: new Adapter() });
复制代码
CRA 会自动搜索这个文件,如果,你没有使用 CRA,这时你就需要在 snapshotSerializers 的相同位置添加这一配置:
"setupFiles": ["./src/setupTests.js"],
复制代码
创建测试文件
Jest 将会在符合以下规则的地方 查找所有的测试文件 :
-
文件夹
__tests__
中以.js
结尾的文件 -
以
.test.js
结尾的文件 -
以
.spec.js
结尾的文件
比较方便的是把每个测试文件和需要测试代码放在同一目录下。这在语义上更加有意义,同时也会让相对路径更加简单(
./MyComponent
vs
../../MyComponent
)。
以下是
MyComponent.test.js
的演示:
import React from 'react';
import { shallow } from 'enzyme';
import MyComponent from './MyComponent';
describe('MyComponent', () => {
it('should render correctly in "debug" mode', () => { const component = shallow(<MyComponent debug />);
expect(component).toMatchSnapshot();
});
});
复制代码
在 CRA 环境下,当执行
npm test
时会运行所有的测试文件并把结果输出到终端。通过自定义标签
-- --testPathPattern filename/
可以指定只运行特定的文件或者使用
-- --testPathIgnorePatterns filename/
来忽略指定的文件。
Mount, Shallow, Render
import { mount, shallow, render } from ‘enzyme';
复制代码
为了测试组件以上的方法肯定会用到,就如上一段代码所示。
Mounting
- 完整的 DOM 渲染,包括子组件
- 对于需要和 DOM 交互、完整测试组件生命周期情况,这是最理想渲染方式
-
因为是真实把组件渲染成 DOM,为了不影响其他的测试,每次测试完成都需要调用
.unmount()
- 允许直接访问传递给 root 组件(包括默认的)和传递给子组件的 props。
Shallow
- 只渲染单个组件,不包括子组件。这对于需要隔离组件做单纯的单元测试非常有用。它可以防止因为子组件的改变和 bugs 影响组件测试的结果。
-
在 Enzyme 3 中
shallow
渲染默认也可以访问组件的生命周期方法。 -
不能访问传递给 root 组件的 props(因为它们不是默认 props),但是,它们可以传递给子组件,也可以测试传递的 props 是否对组件有
影响
。也就说
shallow
可以测试组件的渲染,但不能测试其中的 element。
Render
- 把组件渲染成静态的 HTML,包括子组件
- 不能访问 React 的生命周期方法
-
相比
mount
成本更低,但功能也更少
Testing
基本的组件渲染
一个简单无交互的组件:
it('should render correctly with no props', () => {
const component = shallow(<MyComponent/>);
expect(component).toMatchSnapshot();
});
it('should render banner text correctly with given strings', () => {
const strings = ['one', 'two'];
const component = shallow(<MyComponent list={strings} />);
expect(component).toMatchSnapshot();
});
复制代码
事件
Enzyme API 有好几种方式可以模拟事件或者用户交互。如果,你想测试子组件的交互功能,这时就需要
mount
方法了。
it('should be possible to activate button with Spacebar', () => {
const component = mount(<MyComponent />);
component
.find('button#my-button-one')
.simulate('keydown', { keyCode: 32 });
expect(component).toMatchSnapshot();
component.unmount();
});
复制代码
模拟方法
或许你只是想简单验证下通过 prop 传递的方法是否成功执行。
const clickFn = jest.fn();
describe('MyComponent', () => {
it('button click should hide component', () => {
const component = shallow(<MyComponent