代码覆盖率(也被成为测试覆盖率)统计了源代码的那些行被测试执行过了。关于测试覆盖数据的一个常见误解是:
我的源代码的代码覆盖率很高;因此,我的代码是被充分测试了的。
上面的陈述试错误的!覆盖率高是个必要条件,但不是充分条件。
充分测试的代码 ===> 高覆盖率
充分测试的代码 <=X= 高覆盖率
最常用的覆盖率数据是行覆盖率(statement coverage)。这是收集成本最低的指标,也是最符合直觉的。行覆盖测量了一行代码是否被测试执行过。行覆盖没有测量程序执行路径被覆盖的比例。
行覆盖率的限制
TA不考虑所有可能的数据输入。考虑下面这段代码:
1
int a = b / c;
这段代码可以被b = 18, a = 6覆盖,但没有被c = 0的场景测试过。
一些工具没有提供fractional coverage。例如,在下面的代码中,当条件a为真的时候,代码就已经被100%覆盖了。条件b却没被验证过。
1
2
3if (a || b) {
// do something
}- 测试覆盖分析只能告诉你已存在的代码被覆盖的程度。TA不能告诉你应该存在的代码是否被执行过。考虑下面的代码:这段代码只处理了3个可能返回值中的两个(是个Bug)。缺少了当返回kRecoverableError时的错误回复逻辑。对于哪些只产生kFatalError和kSuccess的测试用例来说,覆盖率就已经达到了100%。对于kRecoverableError的测试用例没有增加测试覆盖率,如果只从覆盖率目标来看像是“多余”的,但是TA能暴露出来一个Bug!
1
2
3
4
5
6
7error_code = FunctionCall();
// return kFatalError, kRecovableError, or kSuccess
if (error_code == kFatalError) {
// handle fatal error, exit
} else {
// assume call succeeded
}
因此做覆盖率分析的正确姿势是:
- 确定你的测试用例足够可读,先不管测试覆盖率。
这意味着写必要的测试用例,而不只是满足测试覆盖率要求的测试用例最小集 - 检查测试的覆盖率结果。找到被测试遗漏的代码。也要注意预期之外的覆盖模式,通常意味着存在Bug。
- 增加测试用例来覆盖2中发现的遗漏。
- 重复执行2-3 直到再增加测试用例就效用降低为止。如果有些边界场景难以测试,考虑把代码重构以提高可测试性。
参考这篇文章: 如何错误使用测试覆盖率
参考文献
https://testing.googleblog.com/2008/03/tott-understanding-your-coverage-data.html