有人说在一般状况下测试 React 组件是没有太大用处的,可是我觉着在一些场景下是颇有必要的:javascript
我尝试过不少的工具组合,可是最终若是会推荐给别的开发者,我更乐意去择推荐以下组合:css
我常常在测试中使用的是浅渲染和 Jest 快照测试。html
在 Jest 进行快照测试java
浅渲染指的是将一个组件渲染成虚拟 DOM 对象,可是只渲染第一层,不渲染全部子组件。因此即便你对子组件作了一下改动却不会影响浅渲染的输出结果。或者是引入的子组件中发生了 bug,也不会对父组件的浅渲染结果产生影响。浅渲染是不依赖 DOM 环境的。node
举个例子:react
const ButtonWithIcon = ({icon, children}) => (
<button><Icon icon={icon} />{children}</button> ); 复制代码
在 React 中将会被渲染成以下:git
<button>
<i class="icon icon_coffee"></i>
Hello Jest!
</button>
复制代码
可是在浅渲染中只会被渲染成以下结果:github
<button>
<Icon icon="coffee" /> Hello Jest! </button>
复制代码
须要注意的是 Icon 组件并未被渲染出来。npm
Jest 快照就像那些带有由文本字符组合而成表达窗口和按钮的静态UI:它是存储在文本文件中的组件的渲染输出。json
你能够告诉 Jest 哪些组件输出的 UI 不会有意外的改变,那么 Jest 在运行时会将其保存到以下所示的文件中:
exports[`test should render a label 1`] = ` <label className="isBlock"> Hello Jest! </label> `;
exports[`test should render a small label 1`] = ` <label className="isBlock isSmall"> Hello Jest! </label> `;
复制代码
每次更改组件时,Jest 都会与当前测试的值进行比较并显示差别,而且会在你作出修改是要求你更新快照。
除了测试以外,Jest 将快照存储在相似 __snapshots __ / Label.spec.js.snap
这样的文件中,同时你须要提交这些文件。
第一步安装全部的依赖包括同版本依赖:
npm install --save-dev jest react-test-renderer enzyme enzyme-adapter-react-16 enzyme-to-json
复制代码
还须要安装 Babel 插件 babel-jest 或者 TypeScript 插件 ts-jest
更新工程的 package.json 文件:
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
},
"jest": {
"setupFiles": ["./test/jestsetup.js"],
"snapshotSerializers": ["enzyme-to-json/serializer"]
}
复制代码
配置项 'snapshotSerializers' 容许你经过配置 'enzyme-to-json',把 Enzyme 的封装类型传给 'Jest' 的快照匹配项中,从而不须要手动进行转化。
建立一个 test/jestsetup.js 的文件来自定义 Jest 的运行环境(上面的 setupFiles 配置项)
import Enzyme, { shallow, render, mount } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
// React 16 Enzyme adapter
Enzyme.configure({ adapter: new Adapter() });
// Make Enzyme functions available in all test files without importing
global.shallow = shallow;
global.render = render;
global.mount = mount;
复制代码
针对 css 模块也能够添加下面的配置到package.json
"jest": {
"moduleNameMapper": {
"^.+\\.(css|scss)$": "identity-obj-proxy"
}
}
复制代码
And run:
同时安装依赖:
npm install --save-dev identity-obj-proxy
复制代码
注意 identity-obj-proxy 依赖的 node 版本是 Node 4或者 Node 5须要开启 'harmony-proxies'
对于大部分没有交互的组件,下面的测试用例已经足够:
test('render a label', () => {
const wrapper = shallow(
<Label>Hello Jest!</Label>
);
expect(wrapper).toMatchSnapshot();
});
test('render a small label', () => {
const wrapper = shallow(
<Label small>Hello Jest!</Label>
);
expect(wrapper).toMatchSnapshot();
});
test('render a grayish label', () => {
const wrapper = shallow(
<Label light>Hello Jest!</Label>
);
expect(wrapper).toMatchSnapshot();
});
复制代码
有的时候若是你想测试的更精确和看到真实的值。那样的话须要在 Enzyme API 中使用 Jest的 断言。
test('render a document title', () => {
const wrapper = shallow(
<DocumentTitle title="Events" />
);
expect(wrapper.prop('title')).toEqual('Events');
});
test('render a document title and a parent title', () => {
const wrapper = shallow(
<DocumentTitle title="Events" parent="Event Radar" />
);
expect(wrapper.prop('title')).toEqual('Events — Event Radar');
});
复制代码
有的时候你不能用快照。好比组件里面有随机ID像下面的代码:
test('render a popover with a random ID', () => {
const wrapper = shallow(
<Popover>Hello Jest!</Popover>
);
expect(wrapper.prop('id')).toMatch(/Popover\d+/);
});
复制代码
你能够模拟相似 'click' 或者 'change'这样的事件而后把组件和快照作比较:
test('render Markdown in preview mode', () => {
const wrapper = shallow(
<MarkdownEditor value="*Hello* Jest!" /> ); expect(wrapper).toMatchSnapshot(); wrapper.find('[name="toggle-preview"]').simulate('click'); expect(wrapper).toMatchSnapshot(); }); 复制代码
有的时候你想要测试一个子组件中一个元素是怎样影响组件的。你须要使用 Enzyme的 mount 方法来渲染一个真实的 DOM。
test('open a code editor', () => {
const wrapper = mount(
<Playground code={code} /> ); expect(wrapper.find('.ReactCodeMirror')).toHaveLength(0); wrapper.find('button').simulate('click'); expect(wrapper.find('.ReactCodeMirror')).toHaveLength(1); }); 复制代码
相似于在事件测试中,由使用快照测试组件的输出呈现替换为使用Jest的mock函数来测试事件处理程序自己:
test('pass a selected value to the onChange handler', () => {
const value = '2';
const onChange = jest.fn();
const wrapper = shallow(
<Select items={ITEMS} onChange={onChange} /> ); expect(wrapper).toMatchSnapshot(); wrapper.find('select').simulate('change', { target: { value }, }); expect(onChange).toBeCalledWith(value); }); 复制代码
Jest使用JSON进行快照测试,所以你能够测试返回JSON的任何函数,方法与测试组件相同:
test('accept custom properties', () => {
const wrapper = shallow(
<Layout flexBasis={0} flexGrow={1} flexShrink={1} flexWrap="wrap" justifyContent="flex-end" alignContent="center" alignItems="center" /> ); expect(wrapper.prop('style')).toMatchSnapshot(); }); 复制代码
调试浅层渲染器输出
Use Enzyme’s debug method to print shallow renderer’s output: 使用Enzyme的调试方法打印千层渲染器的输出:
const wrapper = shallow(/*~*/);
console.log(wrapper.debug());
复制代码
启用覆盖范围的失败测试
当你的测试失败时,带有覆盖范围标志的diff以下所示:
-<Button
+<Component
复制代码
尝试将箭头函数组件替换为常规函数组建:
- export default const Button = ({ children }) => {
+ export default function Button({ children }) {
复制代码
requestAnimationFrame 错误
当你运行你的测试时,你可能会看到以下错误:
console.error node_modules/fbjs/lib/warning.js:42
Warning: React depends on requestAnimationFrame. Make sure that you load a polyfill in older browsers. http://fb.me/react-polyfills
复制代码
React 16依赖于requestAnimationFrame
,所以你须要在你的测试代码中添加一个polyfill
// test/jestsetup.js
import 'raf/polyfill';
复制代码