单元测试

认识单元测试

单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义。

在 web 前端领域,单元测试通常包括:对某个 JS 的方法进行测试,对某个组件进行测试。除了单元测试,前端经常会有端到端测试。相对于端到端测试来说,单元测试编写更复杂。但是完整的单元测试的样例能够覆盖更多端到端测试覆盖不到的点,对于前端代码通常比较关键的模块可以通过添加单元测试来规避后续修改或者重构带来的风险。单元测试样例的编写过程也有助于进一步审视模块的功能。

单元测试适用于功能不会经常改动的工具方法模块和一些基础的公共组件,对于会经常在快速迭代中更新的业务组件和功能模块端到端的测试会更适合,但这并不是说不需要写单元测试。这其实是一个投入和产出比的一个权衡,编写单元测试可能会需要频繁的更新测试样例,对于部分业务尤其是中后台的应用来说成本是偏高的。

本章主要针对单元测试进行介绍,更多的端到端测试的内容还在编写中,尽请期待。

前端怎么做单测

在 React 诞生之前,前端的单元测试往往只能针对于一些纯粹的 JS 模块。由于对浏览器环境的依赖,很难去做涉及到 dom 操作的模块的单元测试。但是对于前端来说,大部分代码其实都是 UI 组件,这就导致长期以来前端的代码甚至一些开源的被应用得很广泛的 UI 组件库都缺乏完整的单元测试。

但是 React 的诞生伴随着虚拟 dom 被发明,这使得前端组件的测试变得更方便了。虚拟 dom 使得一个组件可以脱离真实的浏览器环境模拟 dom 的相关操作。我们可以通过测试虚拟 dom 的表现是否正常来测试组件的逻辑,让编写组件的测试能够脱离对浏览器 dom 环境的依赖。

在 umi 中,内置了 jest 作为单元测试的库,接下来我们会介绍如何使用 jest 对 JS 方法或者组件进行测试。

使用 jest

umi 内置了 jest 测试。执行 umi test 会匹配所有 .test.js 结尾的文件运行。通常我们约定把测试的代码统一放到 test 文件夹下,当然你也可以按照你的习惯组织,比如可以和测试对应的模块放到一起。

配置 jest

jest 是 Facebook 退出的一个开源的测试框架,它有它自己的配置。它约定了它的配置可以在 package.json 中,也可以在项目根目录的 jest.config.js 中。在该课程中我们以 jest.config.js 来做示例,它是 jest 的默认配置文件,当然你也可以 jest 提供的方式指定配置文件,可以参考 jest 的配置文档

那么,让我们在项目的根目录下添加一个 jest.config.js,并填上内容如下:

// 需要注意的是这里不能使用 export default 这样的 ES6 的语法,因为它是被 jest 直接读取的,不会被 umi 编译。
module.exports = {
  testURL: 'http://localhost:7001',
};

其实 jest 的配置不是必须的,在下面的示例中的第一个示例 测试一个方法 中其实是不需要的。但是在 测试一个组件 中因为我们引入了 enzyme 来测试组件。最新版的 enzyme 依赖浏览器的 localStorage 等环境,而 jest 中 testURL 的默认值是 about:blank,这样会导致运行时报错,设置了 testURL 为一个有效的 URL 后能够避免这个问题。当然不一定必须是 http://localhost:7001,只要是合法的 URL 地址即可。不过这不意味着 testURL 是没有意义的,实际上 testURL 还有其他作用,你可以参考它的文档说明查看具体内容。

测试一个方法

jest 在执行测试文件的时候会默认注入一些方法,对于最简单的测试,你只需要了解 testexpect 这两个方法即可。 test 方法接收两个参数,第一个是测试描述,第二个是一个函数,它包裹了一个测试样例。在这个样例中你可以调用 expect 方法检测你的代码。比如,我们新建一个 test/helloworld.test.js 的文件,然后写上如下的内容:

const sum = function (a, b) {
  return a + b;
};

test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
});

package.json 中添加 scripts.testumi test,然后运行 cnpm run test。接下来你就能看到如下的结果:

result

测试一个组件

接下来我们尝试测试一个最简单的组件,首先我们在 src/component 下面新建一个 TestDemo.js 的组件,组件内容如下:

export default () => {
  return <div>test</div>;
};

然后我们在 test/helloworld.test.js 中添加对它的测试。在这之前你需要先安装 测试一个组件 包,它是针对 React 的测试工具库,使得我们可以很方便的利用 React 虚拟 dom 来编写测试样例。

在项目根目录下执行:

cnpm i --save-dev enzyme enzyme-adapter-react-16

然后添加如下的测试样例:

import { mount } from 'enzyme';
import TestDemo from '../src/component/TestDemo';

test('TestDemo', () => {
  const wrapper = mount(<TestDemo />);
  expect(wrapper.find('div').text()).toBe('test');
});

然后运行 cnpm run test 就可以看到结果了。

enzyme 提供了大量的方法可以让你能够测试组件中的内容,更多信息可以参考 enzyme 的官网