本文例子的 GitHub 地址:https://github.com/codelegant/react-action
React 有自己的测试工具:Jest,就像 Angular 自己的 Karama。
使用 Jest 测试 React,主要分两部分,快照测试(Snaptshot Testing)与 DOM 测试(DOM Testing)。
首先安装依赖:
1 |
npm i -D jest babel-jest babel-preset-es2015 babel-preset-react react-test-renderer |
然后配置package.json
与.babelrc。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// package.json "dependencies": { "react": "<current-version>", "react-dom": "<current-version>" }, "devDependencies": { "babel-jest": "<current-version>", "babel-preset-es2015": "<current-version>", "babel-preset-react": "<current-version>", "jest": "<current-version>", "react-test-renderer": "<current-version>" }, "scripts": { "test": "jest" } // .babelrc { "presets": ["es2015", "react"] } |
Snapshot Testing
快照测试第一次运行的时候会将 React 组件在不同情况下的渲染结果(挂载前)保存一份快照文件。后面每次再运行快照测试时,都会和第一次的比较,除非使用npm test -- -u
命令重新生成快照文件。
首先我们得有一个组件,后面的 DOM Testing 也会使用这个组件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// public/src/jest/LinkButton.js import React, { Component } from 'react'; export default class LinkButton extends Component { constructor() { super(); this.state = {liked: false}; this.handleClick = this.handleClick.bind(this); } handleClick() { return this.setState({ liked: !this.state.liked }); } render() { const text = this.state.liked ? 'like' : 'haven\'t liked'; return (<p onClick={this.handleClick}> You {text} this.Click to toggle. </p>); } } |
上面的组件拥有三种状态,初始状态,点击状态,以及再次被点击的状态。依此我们生成三种状态的快照。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// public/src/jest/__tests__/LinkButton.snapshot-test.js import React from 'react'; import renderer from 'react-test-renderer'; import LinkButton from '../LinkButton'; describe('<LinkButton/>', () => { it('Snapshot', () => { const component = renderer.create(<LinkButton/>); let snapshot = component.toJSON(); expect(snapshot).toMatchSnapshot(); snapshot.props.onClick(); snapshot = component.toJSON(); expect(snapshot).toMatchSnapshot(); snapshot.props.onClick(); snapshot = component.toJSON(); expect(snapshot).toMatchSnapshot() }); }); |
然后在控制台运行npm test

就会生成__tests__\__snapshots__\LinkButton.snapshot-test.js.snap
快照文件,打开文件,可以看到文件内容,里面包含组件的三种状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
exports[`<LinkButton/> Snapshot 1`] = ` <p onClick={[Function bound handleClick]}> You haven\'t liked this.Click to toggle. </p> `; exports[`<LinkButton/> Snapshot 2`] = ` <p onClick={[Function bound handleClick]}> You like this.Click to toggle. </p> `; exports[`<LinkButton/> Snapshot 3`] = ` <p onClick={[Function bound handleClick]}> You haven\'t liked this.Click to toggle. </p> `; |
此时,如果我们修改LinkButton.js
文件,比如将p
标签中的文件修改为Me {text} this.Click to toggle
,然后我们再次运行测试,控制台显示测试未通过,并将两次快照不核匹配的位置标记出来。这可以防止无意间修改组件的某些部分。

如果确实需要更新快照文件,使用npm run test -- -u
命令。
DOM Testing
主要测试组件生成的 DOM 节点是否符合预期,比如响应事件之后,组件的属性与状态应该是怎样的。DOM Testing 依赖于官方的 TestUtil(主要用于操作 DOM,模拟事件等等),所以需要安装react-addons-test-utils
。同样,编写对应的测试文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
// public/src/jest/__tests__/LinkButton.dom-test.js import React from 'react'; import ReactDOM from 'react-dom'; import TestUtils from 'react-addons-test-utils'; import LinkButton from '../LinkButton'; describe('<LinkButton/>', () => { const linkButton = TestUtils.renderIntoDocument(<LinkButton />); const linkButtonNode = ReactDOM.findDOMNode(linkButton); it('Mount', () => { //获取初始状态的元素文本 expect(linkButtonNode.textContent).toEqual('You haven\'t liked this.Click to toggle.'); }); it('Clicked', () => { //触发点击事件,并比较文本是否等于预期 TestUtils.Simulate.click(linkButtonNode); expect(linkButtonNode.textContent).toEqual('You like this.Click to toggle.'); }); it('Clicked again', () => { //再次触发点击事件,并比较文本是否等于预期 TestUtils.Simulate.click(linkButtonNode); expect(linkButtonNode.textContent).toEqual('You haven\'t liked this.Click to toggle.'); }); }); |
然后运行测试,控制台显示测试通过。

如果我们你快照测试中那样修改p
元素的文本,再次测试,控制台显示测试不通过。

生成测试覆盖报告(coverage report
)
使用命令npm test -- --coverage
就可以生成测试覆盖报告。

同时,还会在根目录生成一个名为 coverage 的文件夹,是测试覆盖报告的网页版,包含更多,更详细的信息。测试覆盖报告的解读方法参考 stackoverflow 上的问答:How do I read an Istanbul Coverage Report?