测试
代码的终极目标有两个,第一个是实现需求,第二个是提高代码质量和可维护性。测试是为了提高代码质量和可维护性,是实现代码的第二个目标的一种方法。
单元测试
单元测试(Unit Testing),是指对软件中的最小可测试单元进行检查和验证。在结构化编程的时代,单元测试中单元指的就是函数。Beeshell 组件库全面使用单元测试,由组件的开发者完成。研究成果表明,无论什么时候作出修改都需要进行完整的回归测试,对于提供基础功能的组件来说更是如此,在生命周期中尽早地对软件产品进行测试将使效率和质量都得到最好的保证。Bug 发现的越晚,修改它所需的成本就越高,单元测试是一个在早期抓住 Bug 的机会。
单元测试的优点有一下几点:
- 是一种验证行为。程序的每一项功能是测试来验证正确性,为后期的增加功能、代码重构提供了保障。
- 是一种设计行为。单元测试使得我们从调用者的角度观察、思考,迫使开发者把程序设计成易于调用和可测试的,在一定程度上降低耦合性。
- 是一种编写文档的行为。是展示函数、类使用的最佳文档。
Beeshell 组件库使用 Jest 做为单元测试的工具,自带断言、测试覆盖率工具,实现开箱即用。
测试用例设计
测试用例的核心是输入数据,我们会选择就有代表性的数据作为输入数据,主要有三种:正常输入,边界输入,非法输入,下面以组件库中提供的 isLeapYear
工具函数来举例说明,代码如下:
Jest 使用 test
函数来描述一个测试用例,其中的 toBe
边是一句断言。
函数使用了外部数据,正常输入肯定会有,这里的 2000
和 '2000'
都是正常输入。边界输入和非法输入并不是所有的函数都有,这里为了说明使用了有这两种输入的例子,边界输入是有效输入的极限值,这里 0
和 Infinity
是边界输入,非法输入是正常取值范围以外的数据, 'xx'
和 false
则是非法输入。一般情况下,考虑以上三种输入可以找出函数的基本功能点,单元测试与代码编写是“一体两面”的关系,编码时对上述三种输入都是应该考虑的,否则代码的健壮性就会成问题。
上面说的测试是针对程序的功能来设计的,就是所谓的“黑盒测试”。单元测试还需要从另一个角度来设计测试数据,即针对程序的逻辑结构来设计测试用例,就是所谓的“白盒测试”。
还是以 isLeapYear
函数来说明,其代码如下:
这里有一个 if else
语句,如果我们只提供一个 2000
的输入,只会测试到 if
语句,而不会测试的 else
语句。虽然在黑盒测试足够充分的情况下,白盒测试没有必要,可惜“足够充分”只是一种理想状态,难于衡量测试的完整性是黑盒测试的主要缺陷。而白盒测试恰恰具有易于衡量测试完整性的优点,两者之间具有极好的互补性,例如:完成功能测试后统计语句覆盖率,如果语句覆盖未完成,很可能是未覆盖的语句所对应的功能点未测试。
白盒测试也是比较常见的需求,Jest 内置了测试覆盖率工具,可以直接在命令中添加 --coverage
参数便可以输出单元测试覆盖率的报告,结果如下:
可以看到代码的每一行都覆盖到了 Coverage 为 100%,在很大程度上保证了功能的稳定性。
UI 自动化测试
想要确保组件库的 UI 不会意外更改时,快照测试(Snapshot Testing)是非常有用的工具。一个典型的移动 APP 快照测试案例过程是,先渲染 UI 组件,然后截图,最后和独立于测试存储的参考图像进行比较。使用 Jest 进行在快照测试,在 Beeshell 中第一次对某个组件进行测试时,会在测试目录下创建一个 snapshots 文件夹,并将快照结果存放在该文件夹中。快照结果文件以 <组件名>.js.snap 命名,其内容为某个状态下的 UI 组件树。
下面以 Button 组件快照测试为例来说明:
运行命令后得到快照结果:
静态分析
经常与单元测试联系起来的开发活动还有静态分析(Static analysis)。静态分析就是对软件的源代码进行研读,查找错误或收集一些度量数据,并不需要对代码进行编译和执行。
静态分析有用而且快速,可以发现 30%~70% 的代码问题,分分钟检查一遍,成本低、收益高。Beeshell 使用 SonarQube 进行静态代码检查。
SonarQube 是一个开源的代码质量管理系统,支持 25+ 种语言,可以通过使用插件机制与 Eclipse、VSCode 等工具集成,实现对代码的质量的全面自动化分析和管理。
SonarQube 通过对 Reliability(可靠性)、Security(安全性)、Maintainability(可维护性)、Coverage(测试覆盖率)、Duplications(重复)几个维度,对代码进行全方位的分析,通过设置 Quality Gates 保证代码质量。
Beeshell 组件库的分析结果概况如图:
可靠性达到 A 级别,是最高等级,表示无 Bug:
安全性达到 A 级别,是最高等级,表示无漏洞:
测试覆盖率平均达到 70% 以上