原文地址: medium.com/codeclan/te…
译文地址:github.com/xiao-T/note…
本文版权归原作者所有,翻译仅用于学习。
这篇文章将会介绍如何设置并使用 Jest 和 Enzyme 测试通过 Create React App (CRA) 创建的 React 应用。对于那些从头开始的人我们会给出一些建议。但是,不会涉及太多有关 React 的知识。
Jest 和 Enzyme 是两个不同,但又相互相成的工具,整合在一起可以提供更加灵活和更具创造性的测试能力。我们将会简单介绍两者有什么差异。
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 是针对 React 的测试功能库,它可以更容易的断言、操控和遍历 React 组件。
Enzyme,由 Airbnb 创建,并添加了更好的功能方法,比如:渲染组件、查找元素和与元素交互。
在 CRA 中它必须单独安装。
综上所述:
如果不使用 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/
来忽略指定的文件。
import { mount, shallow, render } from ‘enzyme'; 复制代码
为了测试组件以上的方法肯定会用到,就如上一段代码所示。
.unmount()
shallow
渲染默认也可以访问组件的生命周期方法。shallow
可以测试组件的渲染,但不能测试其中的 element。mount
成本更低,但功能也更少一个简单无交互的组件:
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 onClick={clickFn} />); component .find('button#my-button-two') .simulate('click'); expect(clickFn).toHaveBeenCalled(); }); }); 复制代码
慢慢会变得更加复杂,你或许想模拟 MyComponent.js
里面 import 的一个方法,它返回一个值,检查是否被调用,并对比快照。
想象一下,我们在 MyComponent.js
中引入了一个 SaveToStorage 对象,它包含 TryGetValue
和 TrySetValue
两个方法。TryGetValue
有一个默认的返回值:false,如果,它返回 true
说明组件会改变。组件中两个不同按钮会使用它们。
我们用 jest.mock
也可以模拟这种行为,同时,jest.fn
可以覆写组件内的方法。
const mockTryGetValue = jest.fn(() => false); const mockTrySetValue = jest.fn(); jest.mock('save-to-storage', () => ({ SaveToStorage: jest.fn().mockImplementation(() => ({ tryGetValue: mockTryGetValue, trySetValue: mockTrySetValue, })), })); describe('MyComponent', () => { it('should set storage on save button click', () => { mockTryGetValue.mockReturnValueOnce(true); const component = mount(<MyComponent />); component.find('button#my-button-three').simulate('click'); expect(mockTryGetValue).toHaveBeenCalled(); expect(component).toMatchSnapshot(); component.unmount(); }); }); 复制代码
在这,只介绍了其中一部分,Jest 还有其它的断言方法(包括:toEqual
),同时 Enzyme 还有更多的遍历和交互的方法(包括:first
和 setProps
)。每个文档介绍的都很好,并提供了更多的可能。
同时使用 Jest 和 Enzyme 让测试 React 组件更加容易,同时测试更具可读性。
多谢你们喜欢!🙂
如果,你喜欢这篇文章,你还可以看看这些:
除了官方文档和我自己的经验,我发现下面两篇文章也非常有用,并且,我写的这篇文章也受到它们的启发同时也巩固了我的知识:
除单元测试和集成测试之外,你还希望看看视觉回归测试,我推荐你关注 Differencify。