代码覆盖率
本节阅读量:上一课中,我们讨论了如何编写和保存简单测试。本课将讨论编写哪些类型的测试有助于确保代码正确。
代码覆盖率
术语代码覆盖率用于描述测试时执行了多少程序源代码。代码覆盖率有许多不同的衡量方式。在下面的部分中,我们将介绍几个有用且常见的方法。
语句覆盖率
术语语句覆盖率是指代码中被测试例程执行过的语句所占的百分比。
考虑以下函数:
|
|
调用foo(1, 0)将为该函数提供完整的语句覆盖,因为函数中的每条语句都会被执行。
对于isLowerVowel()函数:
|
|
该函数需要两次调用才能测试所有语句,因为在同一次函数调用中无法同时到达语句2和语句3。
虽然以100%语句覆盖率为目标是好的,但这通常不足以确保代码正确。
分支覆盖率
分支覆盖率是指已执行分支所占的百分比,每个可能的分支都会单独计数。if语句有两个分支——一个分支在条件为true时执行,另一个分支在条件为false时执行(即使没有对应的else语句要执行)。switch语句可以有多个分支。
|
|
前面对foo(1, 0)的调用为我们提供了100%的语句覆盖率,并测试了x > y的用例,但这只给了我们50%的分支覆盖率。我们还需要调用一次foo(0, 1),来测试if语句不执行的用例。
|
|
在isLowerVowel()函数中,需要两个调用才能提供100%的分支覆盖率:一个(如isLowerVowel(‘a’))用于测试第一个情况,另一个(如isLowerVowel(‘q’))用于测试default情况。多个case标签对应同一组执行语句时,只需要为其中一个case标签编写测试用例即可——如果这个用例能正常工作,其他并列的case标签通常也可以。
现在考虑以下函数:
|
|
这里需要3次调用才能获得100%的分支覆盖率:compare(1, 0)测试第一个if语句的肯定用例;compare(0, 1)测试第一个if语句的否定用例和第二个if语句的肯定用例;compare(0, 0)测试第一个和第二个if语句的否定用例,并执行else语句。因此,可以说该函数通过3次调用得到了可靠测试(略优于1.8千亿亿次)。
最佳实践
目标是代码的100%分支覆盖率。
循环覆盖率
循环覆盖率(非正式地称为0、1、2测试)表示,如果代码中有循环,则应确保它在迭代0次、1次和2次时都能正常工作。如果它在2次迭代时正确工作,那么它通常也应该能在大于2次的所有迭代中正确工作。因此,这三个测试覆盖了所有关键可能性(因为循环不能执行负次数)。
考虑以下程序:
|
|
要在该函数中正确测试循环,应该调用它三次:spam(0)测试零次迭代用例,spam(1)测试一次迭代用例,spam(2)测试两次迭代用例。如果参数2有效,则参数n通常也应该有效,其中n > 2。
最佳实践
使用0、1、2测试来确保循环在不同的迭代次数下正确工作。
测试不同类别的输入
在编写接受参数的函数时,或者在接受用户输入时,需要考虑不同类别的输入会发生什么。在这种情况下,我们使用术语“类别”来表示具有类似特征的一组输入。
例如,如果我编写了一个用于计算整数平方根的函数,那么用哪些值来测试它比较有意义?您可能会从一些正常值开始,例如4。但使用0和负数进行测试也是一个好主意。
下面是类别测试的一些基本准则:
对于整数,请确保考虑函数如何处理负值、零值和正值。如果可能,还应该检查溢出。
对于浮点数,请确保考虑函数如何处理存在精度问题的值(比预期稍大或稍小的值)。用于测试的double值可以选择0.1和-0.1(用于测试比预期稍大的数字,如果预期是0.000x),以及0.6和-0.6(用于测试略小于预期的数字,如果预期是xxxxx.yy)。
对于字符串,请确保考虑函数如何处理空字符串、含字母和数字的字符串、包含空格的字符串(前导、尾随和内部空格),以及全部为空格的字符串。
如果函数采用指针,也不要忘记测试nullptr(将在后续介绍)。
最佳实践
测试不同类别的输入值,以确保您的代码单元正确处理它们。