在软件开发过程中,代码覆盖率常常被用作衡量代码质量的一个重要指标。一些开发者甚至以100%的代码覆盖率为荣,认为这是代码质量的最佳证明。然而,是否真的需要100%的代码覆盖率来证明代码质量呢?
首先,需要了解什么是代码覆盖率。代码覆盖率是指在执行单元测试时,测试用例能够覆盖到的代码行数的比例。要达到100%的代码覆盖率,开发者需要付出大量的努力编写测试用例,并确保所有的代码路径都被覆盖,这无疑是非常耗时的。
那么,如果不追求100%的代码覆盖率,80%或75%的覆盖率是否更现实或更平衡呢?让回到测试的基本原则,问自己一个基本的测试问题:为什么要进行测试?
进行测试是因为存在复杂的代码,这些代码可能会导致缺陷。因此,需要确保这些复杂的代码有测试用例,并且已经被测试过,缺陷在上线前已经被修复。
在这里,可以看到“复杂”这个词被强调了。是的,这应该是驱动代码覆盖率的关键因素。换句话说,确保测试覆盖了代码的复杂部分。
那么,如何定义“复杂”呢?对于某些人来说复杂的东西,对于其他人来说可能很简单。那么,如何判断代码的复杂性呢?
代码行数多就意味着代码复杂吗?大量的IF和SELECT CASE、FOR循环会使代码复杂吗?应该以循环复杂度作为基准吗?如果对循环复杂度不熟悉,可以阅读这里:
大量的继承、聚合和组合会使代码复杂吗?认为应该有一个指标,能够考虑所有上述因素。“维护性指数”就是这样一个指标,它是由Visual Studio代码度量分析器提供的。维护性指数指标考虑了所有上述因素,并得出一个介于0到100之间的数值,数值越大越好。
所以,希望看到维护性指数较低的代码被100%覆盖。例如,在下面的报告中,希望看到“Calculate”方法有100%的测试覆盖率,因为它的维护性指数是65。但是“Cancel”方法的维护性指数值是80,所以可能会排除它。
如果查看“Cancel”方法的代码,它只是初始化了很多变量。所以测试这个方法只会增加单元测试的工作量,而不会有很大的质量提升。
public void Cancel()
{
Num1 = 0;
Num2 = 0;
Operation = "";
Pie = 3.14;
Num5 = 0;
Operation1 = "";
Num20 = 0;
Numx = 0;
OperationStart = "";
PieMore = 3.145;
Num6 = 0;
Operationnew = "";
}
现在看看“Calculate”方法,它很复杂,有很多“IF”条件等。所以希望确保这个方法的每个代码分支都被执行。
public int Calculate()
{
if (Operation.Length == 0)
{
throw new Exception("Operation not specified");
}
if (Operation == "+")
{
return Num1 + Num2;
}
else if (Operation == "-")
{
return Num1 - Num2;
}
else
{
return Num2 * Num1;
}
return 0;
}
所以,与其追求100%的代码覆盖率,建议遵循以下过程: