单元测试
单元测试是指的对软件中最小单元进行测试和验证。
我理解,单元测试一般是对于业务逻辑的测试,不论是业务逻辑组件还是 UI 库,校验的目的都是验证具体执行逻辑是否正确。
jest 测试
jest 单测需要 enzymejs.github.io/enzyme/ 配合完成,测试分为快照测试和 DOM 测试两种。
快照测试
快照测试会生成一份快照文件,后面的结果运行时都会和前面的结果进行比较。快照组件适合进行 UI 渲染的测试,防止渲染出现大变动,导致问题发生。
Dom测试
DOM 测试是将业务组件通过渲染运行,把得到的结果与预期进行比较发现问题,适合验证一些具体的业务逻辑。运行要点有:1 模拟用户测试环境,触发对应事件。2 监控对应输出数据是否符合要求。
jest 组件渲染方式
- shallow
浅渲染,一层不涉及子组件,虚拟dom 渲染速度快。 - mount
深渲染,涉及子组件,渲染速度慢。 一般来说能先用 shallow 就用 shallow,为了测试的性能考虑。
编码细节
事件触发
事件的触发分为三种:
- react 事件
react 是自己维护事件机制管理的,它只会把事件绑定到具体的 document 节点上,然后自己进行处理,具体可以看看 zhuanlan.zhihu.com/p/25883536。 那么如何触发呢?触发react节点相关事件就需要Enzyme的simulate函数具体例子如下:
// 1 组件
<input
value={value}
onChange={handleChange}
/>
// 2 组件内部
const { onChange } = this.props;
const handleChange = (e: Event) => {
const val = e.target.value;
setValue(val);
if (onChange) {
onChange(e, val);
}
};
// 3 测试代码
it('fires onChange event', () => {
const handleChange = jest.fn();
const wrapper = mount(<Input onChange={handleChange} />);
wrapper.find('input').simulate('change', { target: { value: 'test' } });
expect(handleChange).toBeCalled();
expect(wrapper.find('input').props().value).toEqual('test');
});
-
dom事件
假设业务中遇到了真实的dom绑定了某个事件,那么你需要利用浏览器的 EventTarget.dispatchEvent 方式进行触发,具体示例如下:
test('测试 dom 事件', () => {
const ele = ReactDOM.findDOMNode(
wrapper.find('.xselect').instance(),
);
ele.dispatchEvent(new Event('click', { bubbles: true }));
wrapper.unmount();
});
- 普通事件 如果传入了普通事件,可以尝试直接获取事件然后调用。示例代码如下:
it('测试编辑表单函数', async () => {
await act(async () => {
const wrapper = mount(
<FieldForm />,
);
const onChange =
wrapper.find('FormFieldInput').prop('onChange') || noop;
onChange({
target: {
value: '输入测试名称1',
},
});
});
});
如何处理请求
mock 请求,因为我们注重的是业务逻辑。这里 mock 的方法有三种(本质一样,都是替换,就是具体应用时替换的范围不同)
- 直接 mock 业务函数
- 直接通过 jest.spyOn() mock 公共函数统一解决
- 运用第三方工具包
遇到属性变化等如何进行触发
jest 如果遇到渲染属性在断言之后的问题可以使用 act 函数。因为 act 能确保两件事情,即在 act 的 scope 中。
any state updates will be executed
any enqueued effects will be executed
引自 github.com/threepointo…
覆盖率问题
不要为了覆盖率而覆盖率,否则会对维护增添很多负担。比如下面的代码:
/* istanbul ignore next */
export const noop: () => void = () => {
// do nothing.
};
其他问题
- 如果环境特殊有多个 react 实例? 直接改写 resolver 属性,将 react 指定一个版本的实例。具体直接参照链接代码即可,可以输出console观察具体属性。