# 简介

现在最常见的 React 测试方案还是 Jest,它基本上提供了一个全面的测试框架所需要的一切。 你可以使用 react-test-renderer 在你的 Jest 测试中渲染 React 组件。这已经足以使用 Jest 执行所谓的 Snapshot Tests 了:一旦运行测试,就会创建 React 组件中渲染的 DOM 元素的快照。当你在某个时间点再次运行测试时,将创建另一个快照,这个快照会和前一个快照进行 diff。如果存在差异,Jest 将发出警告,你要么接受这个快照,要么更改一下组件的实现。

  • 最近 React Testing Library (RTL) 也比较流行(在 Jest 测试环境中使用),它可以为 React 提供更精细的测试。RTL 支持让渲染组件模拟 HTML 元素上的事件成,配合 Jest 进行 DOM 节点的断言。

  • 如果你正在寻找用于 React 端到端 (E2E) 测试的测试工具,Cypress 是现在最受欢迎的选择。

# 主流分类

  • 静态测试: 在编写代码逻辑阶段时进行报错提示。(代表库: eslint、flow、TypeScript)
  • 单元测试: 在奖杯模型中, 单元测试的职责是对一些边界情况或者特定的算法进行测试。(代表库: jest、mocha)
  • 集成测试: 模拟用户的行为进行测试, 对网络请求、获取数据库的数据等依赖第三方环境的行为进行 mock。(代表库: jest、react-testing-library)
  • e2e 测试: 模拟用户在真实环境上操作行为(包括网络请求、获取数据库数据等)的测试。(代表库: cypress)

# E2E测试

react+cypress (opens new window)

# 单元测试

单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。单元测试是由程序员自己来完成,最终受益的也是程序员自己。执行单元测试,就是为了尽量证明这段代码的行为和期望的一致。

其实我们每天都在做单元测试,包括那些认为自己从来没有写过单元测试的同事。你写了一个函数,log一下或者在界面上点一下,这,也是单元测试,把这种单元测试称为临时单元测试。临时单元测试的软件,一个是很难能够覆盖所有场景,二个是无法自动化运行,大幅度提高后期测试和维护成本。可以说,进行充分的单元测试,是提高软件质量,降低开发成本的必由之路。

# 优点

能够用更低的成本去验证代码的稳定性,基本保证目标代码在之后一直按照初始的预期运行,同时可以接入持续集成,进行低成本的重复使用,在第一时间发现问题,减少维护成本。

// 验证 keepInTarget 辅助方法是否能够永远保证返回的值为 adns 和 targets 的交集
  // 如果在界面上测试这个功能,进行一次操作的时间就已经够写出下面的测试用例了
  it.each([
    // eslint-disable-next-line no-sparse-arrays
    [, [], undefined],
    [[], [], []],
    [['abc'], [], []],
    [[], ['abc'], []],
    [['abc'], ['abc'], ['abc']],
    [['abc', 'bcd'], ['abc', 'cde'], ['abc']],
  ])('keepInTarget %#', (adns, targets, expectValue) => {
    expect(keepInTarget(adns as any, targets)).toEqual(expectValue);
  });
1
2
3
4
5
6
7
8
9
10
11
12
13

能够帮助开发者从另一个角度去思考如何组织代码,让代码结构更合理——你们有没有见过1000多行的类,方法里面全局状态内部属性随便使用,三个屏都看不完一个方法的那种。 主要体现在:能够促进思考方法、函数的边界,而不是不管三七二十一都放在一个方法里面;能够下意识的去写纯函数;

# 什么时候开始写单元测试

建议大家现在就开始尝试写单元测试。单元测试应该是对代码无侵入的,有没有单元测试都不影响你的代码功能实现。也就是说你完全可以有多少时间写多少测试,有写一个测试的时间就添加一个测试,没有时间就不写。

理由就是上面的第二点,只要你计划写单元测试,即使最后由于各种原因并没有写单元测试,你的代码也会和以前不同。相信我,我读书多不会骗你的

# 明确测试范围

在开始写单元测试之前,首先需要明确当前的测试是需要测试什么?

以组件为例,一般一个组件都会包含:ui呈现、工具方法、内部逻辑处理、第三方依赖、自己写的子组件。那么这么多东西哪些需要测试,哪些不需要测试,你在写测试之前就需要想好。

建议按照这样一个优先级顺序添加测试:关键逻辑代码、复杂逻辑代码、工具方法、其他代码。根据自己的时间,逐步提高测试覆盖率。

在实际操作上可以尝试在自测的环节,将盲目的界面自测操作,转变为单元测试代码,这样甚至可以用更少的时间得到更好的自测效果。

# 框架选型

测试组件库有很多,这里选用了目前最流行的: jest + enzyme (部分示例使用了 @testing-library/*),同时为了测试 hook 还使用了@testing-library/react-hooks

jest 作为一款测试框架,拥有测试框架该有的一套体系,丰富的断言库,大多数api与老牌的测试框架如jasmine、mocha,譬如常用的expect、test(it)、toBe等,都非常好用。内部也是使用了jasmine作为基础,在其上封装。但是因为Snapshot这个特色功能,非常适合react项目的测试。

enzyme 提供了几种方式将react组件渲染成真实的dom,提供了类似jquery的api来获取dom;提供了simulate函数来模拟事件触发;提供接口让我们获取到组件的state和props并且能对其进行操作。enzyme其实是react-test-renderer的封装,react-test-renderer的api非常不友好,但是enzyme开发的api跟jquery一致

@testing-library/react-hooks是一个专门用来测试React hook的库。我们知道虽然hook是一个函数,可是我们却不能用测试普通函数的方法来测试它们,因为它们的实际运行会涉及到很多React运行时(runtime)的东西,因此很多人为了测试自己的hook会编写一些TestComponent来运行它们,这种方法十分不方便而且很难覆盖到所有的情景。

关于使用的框架建议:

  • 如果只是想测试一下功能函数,引入 jest 及其相关依赖即可
  • 如果还想对 react 组件进行测试,添加引入 enzyme 及其相关依赖
  • 如果还想对 hook 进行测试,添加引入 @testing-library/react-hooks 及其相关依赖
  • 建议先都添加上,毕竟不知道什么时候就用的上了,到时候可不一定有时间去调整配置参数

# 参考

Jest + React Testing Library 单测总结 (opens new window)

React 单元测试实践 (opens new window)