Unit Testing

关于组件库单元测试

由 Whiskeyi 于 2023-02-05 发布
全文 1.6k 字, 阅读约需 5 分钟
浏览

Unit Testing

单元测试概念

在计算机编程中,单元测试是一种软件测试方法,通过该方法测试源代码的各个单元(一个或多个计算机程序模块的集合以及关联的控制数据、使用过程和操作过程)[在组件库中对应的即为各个组件]以确定它们是否适合使用。

以上摘自维基百科

本篇主要介绍组件库的单元测试

为什么组件库需要单元测试

在编写高质量的自定义组件过程中,单元测试是永远避不开的一个话题。完善的测试用例是提高自定义组件可用性的保证,同时测试代码覆盖率也是必不可少的一个环节。

大家可以看到,在大部分优秀组件库源码中,都存在单元测试的代码编写。那么首先提出一个问题:为什么组件库需要单元测试?

在我理解下有以下几种原因:

  1. 保证代码的正确性。对于每次(自己/别人)代码修改,能够更有信心
  2. 能够定位排查隐藏bug,减少bug数目,避免回归等不必要的人力浪费
  3. 自动检测,可以做到一次编写,多次运行,节省重复测试的时间
  4. 对于组件库场景下有利于代码的重构,保证重构的安全性

单测的目的

单测的目的不应该是盲目提高覆盖率,而是要写关键代码的关键测试来减少bug出现的概率,以及防止bug回归

与一般库的单元测试有所不同,UI 组件库中的组件涉及到大量渲染和 DOM 交互,因此我们应确保:

  • 保证组件的渲染稳定,如果有所更改,应当是预期中的。
  • 保证组件的功能正常,事件执行、DOM、内部State等等;
  • 保证组件代码逻辑被充分测试,代码覆盖率尽可能达到 100%;

单测核心思路

  1. 关注粒度、代码的边界值和核心逻辑,并非所有逻辑都需要编写单测用例

    当组件对应逻辑修改后,单测用例也需要同步更新。粒度过粗,无法保障代码质量和稳定性;粒度过细,往往不堪需求变更之扰,付出不必要的时间成本。对组件的每一处细节都编写具体的单测用例,这是一件耗时耗精力的事,脱离了编写单测的本质

  2. 对于编写用例,仅需关注输入和输出即可

    对于一个用例,我们需要关注其输入和输出,并且多次输出结果是一致的,这也是单测的基础。

  3. 单一测试职责

    每个用例都是单一测试职责的

  4. 无需为依赖(接口返回值、依赖包…)编写测试用例

    编写的关注点应为源码本身,主要确保源代码自身的逻辑正确性

如何编写组件库的单元测试

主要以 jest@testing-library/react 进行介绍

安装配置jest

jest配置教程

编写测试脚本

一般组件库的测试脚本是在组件文件夹中创建__tests__目录,__tests__目录用于存放测试脚本。

在目录下创建index.test.tsx文件,在index.test.tsx文件中编写测试用例

如:__tests__/index.test.tsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 引入测试组件
import React from 'react';
import { ComponentName } from '..';
// 使用@testing-library/react渲染对应组件
import { render } from '@testing-library/react';

// 测试用例编写,此为伪代码
describe('ComponentName', () => {
// it('', ...)第一个参数字符串内描述测试用例,请用英文描述准确、清晰
it('test case1 for title text', () => {
// container.firstChild为React渲染的对应DOM节点
const { container } = render(<ComponentName title='123' {...props} />);
// toMatchSnapshot为执行快照对比
expect(container.firstChild).toMatchSnapshot();
});
it('test case2 for click', () => {
const onClick = jest.fn();
const { container } = render(<ComponentName title='123' {...props} />);
container.firstChild.click();
expect(onClick).toBeCalled();
});
...
});

test(),expect()均为jest暴露的全局方法,完整用法请参阅: APIExpect

关于快照测试

快照测试相关文档

如上述代码所示.toMatchSnapshot()会在执行测试用例时进行快照对比。快照测试能确保你的UI不会有意外的改变。在快照测试用例首次执行时,会在 __snapshots__目录下创建快照文件,当后续再次执行此测试用例时,会将已有的快照文件进行对比。如果对比成功,则测试通过。

生成的快照代码示例(能够具体描述DOM渲染UI):

1
2
3
4
5
6
7
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`ComponentName test case1 for title text 1`] = `
<div>
123
</div>
`;

快照测试补充说明

  1. 视快照如代码。快照文件__snapshots__应当作源码对待进行提交(我们需要在代码PR中审核快照,改掉测试快照失败时就重新生成快照的习惯,应定位问题后解决

  2. 尽可能详细描述你创建的快照,以便于后续其他人理解

  3. 对于快照失败处理。如快照内容改动比较少,则同步修改快照文件内容;如快照内容变化非常多,可以删掉快照文件的内容,再次执行测试即可(需人工校验,谨慎对待)

相关Script

执行全部用例

执行npx jestjest会自动扫描项目中所有的__tests__文件夹,执行内部的用例。

执行全部用例script

如图所示能够看到这次执行用例的具体情况

执行指定用例

执行npx jest src/components/buttonsrc/components/button为组件对应的相对路径,当修改了单一组件后为单一组件执行测试用例能大量减少测试时间

执行指定用例script

执行代码覆盖率检测

执行npx jest --coverage,会在终端返回此次代码检测覆盖率情况,同时对应在项目根目录生成coverage文件夹
执行代码覆盖率检测

我们可以打开(推荐使用VS Code的Live Server插件)coverage/**/index.html,在Web端查看对应代码的覆盖率情况

打开的效果如图所示:

代码覆盖率查看

在这里可以点击进入对应组件查看单行代码执行情况和次数

执行具体情况