IntelliJ 代码覆盖率高,为何线上bug 频出?
2024-07-14 18:08:57
如何逼 IntelliJ 进行全面的分支/条件覆盖率扫描?
你是否遇到过这样的情况:辛辛苦苦加班写测试,IntelliJ 的代码覆盖率报告显示你的测试覆盖率已经很高了,可是线上bug却依然频出?你是否怀疑过,IntelliJ 的代码覆盖率报告是不是在糊弄你?
没错,你很有可能被 IntelliJ 骗了!它默认的覆盖率分析工具就像一个目光短浅的考官,只关注每个条件语句的每个分支是否被执行过(分支覆盖率),而忽略了每个条件表达式是否都被测试过真假(条件覆盖率)。
举个栗子
假设我们正在开发一个计算员工税后工资的 NetPay
类:
class NetPay {
/**
* Computes the net pay of an employee given certain conditions.
* @param hours - the number of hours worked.
* @param rate - the hourly rate.
* @param vacation - whether the employee is on vacation.
* @param stateTax - whether the employee is subject to state tax.
* @param federalTax - whether the employee is subject to federal tax.
* @return the net pay.
*/
static double computeNetPay(double hours, double rate, boolean vacation,
boolean stateTax, boolean federalTax) {
double grossPay = hours * rate;
if (vacation) {
grossPay *= 2;
}
if (stateTax) {
grossPay *= 0.85;
}
if (federalTax) {
grossPay *= 0.80;
}
return grossPay;
}
}
我们编写了以下测试用例:
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
class NetPayTester {
@Test
void testComputeNetPay() {
assertEquals(300, NetPay.computeNetPay(20, 15, false, false, false));
assertEquals(204, NetPay.computeNetPay(20, 15, false, true, true));
assertEquals(600, NetPay.computeNetPay(20, 15, true, false, false));
// assertEquals(240, NetPay.computeNetPay(20, 15, false, false, true));
// assertEquals(255, NetPay.computeNetPay(20, 15, false, true, false));
// assertEquals(480, NetPay.computeNetPay(20, 15, true, false, true));
// assertEquals(510, NetPay.computeNetPay(20, 15, true, true, false));
// assertEquals(408, NetPay.computeNetPay(20, 15, true, true, true));
}
}
IntelliJ 看到我们勤勤恳恳地测试了多种情况,开心地宣布我们的代码分支覆盖率高达 100%!然而,事实并非如此。computeNetPay
方法中有三个条件语句,每个语句都有两个可能的结果(true 或 false),总共有 2 * 2 * 2 = 8 种可能的执行路径。而我们的测试用例只覆盖了其中的三种,还有五种未被覆盖。
就好比,我们只是测试了员工在三种情况下(不休假不交税、不休假交两种税、休假不交税)的工资计算,却忽略了其他五种情况(比如休假只交一种税)。这就好比在设计汽车安全气囊时,只测试了正面碰撞的情况,却没有测试侧面碰撞和追尾,风险可想而知!
如何逼 IntelliJ 现出原形?
为了让 IntelliJ 原形毕露,我们需要祭出两件法宝:
-
JaCoCo:火眼金睛
JaCoCo 是一款强大的代码覆盖率统计工具,它不仅能看到分支覆盖率,还能看到条件覆盖率,就像火眼金睛一样,让那些漏网之鱼无所遁形。
-
更全面的测试用例:天罗地网
为了达到 100% 的条件覆盖率,我们需要为每个条件表达式编写测试用例,使其分别被评估为 true 和 false,就像编织一张天罗地网,确保任何一种情况都逃不掉。
例如,我们可以添加以下测试用例:
assertEquals(240, NetPay.computeNetPay(20, 15, false, false, true));
assertEquals(255, NetPay.computeNetPay(20, 15, false, true, false));
assertEquals(480, NetPay.computeNetPay(20, 15, true, false, true));
assertEquals(510, NetPay.computeNetPay(20, 15, true, true, false));
assertEquals(408, NetPay.computeNetPay(20, 15, true, true, true));
总结
在 IntelliJ 中,仅仅依靠默认的代码覆盖率报告就像只做了视力检查,很容易遗漏潜在问题。我们需要借助 JaCoCo 这副“火眼金睛”,并通过更全面的测试用例编织“天罗地网”,才能逼迫 IntelliJ 进行全面的分支/条件覆盖率扫描,提高代码质量,减少线上bug,才能睡个安稳觉!
常见问题解答
1. 为什么我需要关注条件覆盖率?
条件覆盖率可以帮助你发现那些隐藏在分支覆盖率背后的逻辑错误。仅仅测试每个分支是否被执行过是不够的,还需要确保每个条件表达式都被测试过真假。
2. JaCoCo 如何与 IntelliJ 集成?
你可以在 IntelliJ IDEA 的插件市场中搜索并安装 JaCoCo 插件。安装完成后,你就可以在 IntelliJ IDEA 中直接运行 JaCoCo 并查看覆盖率报告。
3. 如何编写更全面的测试用例?
在编写测试用例时,你需要考虑所有可能的输入组合,并确保每个条件表达式都被测试过 true 和 false。可以使用等价类划分、边界值分析等方法设计测试用例。
4. 100% 的代码覆盖率就意味着没有 bug 了吗?
并非如此。代码覆盖率只能反映你的代码被测试的程度,并不能保证你的代码逻辑完全正确。
5. 除了 JaCoCo,还有哪些代码覆盖率工具?
除了 JaCoCo,还有 Cobertura、Emma 等代码覆盖率工具。它们的功能和使用方法略有不同,你可以根据自己的需要选择合适的工具。