在撩单元测试代码覆盖率

在做单元测试时,代码覆盖率常常被拿来作为衡量测试好坏的指标,甚至,用代码覆盖率来考核测试任务完成情况,比如,代码覆盖率必须达到80%或 90%。于是乎,测试人员费尽心思设计案例覆盖代码。用代码覆盖率来衡量,有利也有有弊。本文我们就代码覆盖率展开讨论,也欢迎同学们踊跃评论。

首先,让我们先来了解一下所谓的“代码覆盖率”。我找来了所谓的定义:

代码覆盖率 = 代码的覆盖程度,一种度量方式。

上面简短精悍的文字非常准确的描述了代码覆盖率的含义。而代码覆盖程度的度量方式是有很多种的,这里介绍一下最常用的几种:

1. 语句覆盖(StatementCoverage)

又称行覆盖(LineCoverage),段覆盖(SegmentCoverage),基本块覆盖(BasicBlockCoverage),这是最常用也是最常见的一种覆盖方式,就是度量被测代码中每个可执行语句是否被执行到了。这里说的是“可执行语句”,因此就不会包括像C++的头文件声明,代码注释,空行,等等。非常好理解,只统计能够执行的代码被执行了多少行。需要注意的是,单独一行的花括号{} 也常常被统计进去。语句覆盖常常被人指责为“最弱的覆盖”,它只管覆盖代码中的执行语句,却不考虑各种分支的组合等等。假如你的上司只要求你达到语句覆盖,那么你可以省下很多功夫,但是,换来的确实测试效果的不明显,很难更多地发现代码中的问题。

这里举一个不能再简单的例子,我们看下面的被测试代码:

int foo(int a, int b)
{
return  a / b;
}

假如我们的测试人员编写如下测试案例:

TeseCase: a = 10, b = 5

测试人员的测试结果会告诉你,他的代码覆盖率达到了100%,并且所有测试案例都通过了。然而遗憾的是,我们的语句覆盖率达到了所谓的100%,但是却没有发现最简单的Bug,比如,当我让b=0时,会抛出一个除零异常。

正因如此,假如上面只要求测试人员语句覆盖率达到多少的话,测试人员只要钻钻空子,专门针对如何覆盖代码行编写测试案例,就很容易达到主管的要求。当然了,这同时说明了几个问题:

1.主管只使用语句覆盖率来考核测试人员本身就有问题。

2.测试人员的目的是为了测好代码,钻如此的空子是缺乏职业道德的。

3.是否应该采用更好的考核方式来考核测试人员的工作?

为了寻求更好的考核标准,我们必须先了解完代码覆盖率到底还有哪些,如果你的主管只知道语句覆盖,行覆盖,那么你应该主动向他介绍还有更多的覆盖方式。比如:

2. 判定覆盖(DecisionCoverage)

又称分支覆盖(BranchCoverage),所有边界覆盖(All-EdgesCoverage),基本路径覆盖(BasicPathCoverage),判定路径覆盖(Decision-Decision-Path)。它度量程序中每一个判定的分支是否都被测试到了。这句话是需要进一步理解的,应该非常容易和下面说到的条件覆盖混淆。因此我们直接介绍第三种覆盖方式,然后和判定覆盖一起来对比,就明白两者是怎么回事了。

3. 条件覆盖(ConditionCoverage)

它度量判定中的每个子表达式结果true和false是否被测试到了。为了说明判定覆盖和条件覆盖的区别,我们来举一个例子,假如我们的被测代码如下:

int foo(int a, int b)
{
if (a < 10 || b < 10) // 判定
{
return 0; // 分支一
}
else
{
return 1; // 分支二
}
}

设计判定覆盖案例时,我们只需要考虑判定结果为true和false两种情况,因此,我们设计如下的案例就能达到判定覆盖率100%:

TestCaes1: a = 5, b = 任意数字  覆盖了分支一
TestCaes2: a = 15, b = 15          覆盖了分支二

设计条件覆盖案例时,我们需要考虑判定中的每个条件表达式结果,为了覆盖率达到100%,我们设计了如下的案例:

TestCase1: a = 5, b = 5       true,  true
TestCase4: a = 15, b = 15   false, false

通过上面的例子,我们应该很清楚了判定覆盖和条件覆盖的区别。需要特别注意的是:条件覆盖不是将判定中的每个条件表达式的结果进行排列组合,而是只要每个条件表达式的结果true和false测试到了就OK了。因此,我们可以这样推论:完全的条件覆盖并不能保证完全的判定覆盖。比如上面的例子,假如我设计的案例为:

TestCase1: a = 5, b = 15  true,  false   分支一
TestCase1: a = 15, b = 5  false, true    分支一

我们看到,虽然我们完整的做到了条件覆盖,但是我们却没有做到完整的判定覆盖,我们只覆盖了分支一。上面的例子也可以看出,这两种覆盖方式看起来似乎都不咋滴。我们接下来看看第四种覆盖方式。

4. 路径覆盖(PathCoverage)

又称断言覆盖(PredicateCoverage)。它度量了是否函数的每一个分支都被执行了。 这句话也非常好理解,就是所有可能的分支都执行一遍,有多个分支嵌套时,需要对多个分支进行排列组合,可想而知,测试路径随着分支的数量指数级别增加。比如下面的测试代码中有两个判定分支:

int foo(int a, int b)
{
int nReturn = 0;
if (a < 10)
{// 分支一
nReturn += 1;
}
if (b < 10)
{// 分支二
nReturn += 10;
}
return nReturn;
}

对上面的代码,我们分别针对我们前三种覆盖方式来设计测试案例:

a. 语句覆盖

TestCase a = 5, b = 5   nReturn = 11

语句覆盖率100%

b. 判定覆盖

TestCase1 a = 5,   b = 5     nReturn = 11

TestCase2 a = 15, b = 15   nReturn = 0

判定覆盖率100%

c. 条件覆盖

TestCase1 a = 5,   b = 15   nReturn = 1

TestCase2 a = 15, b = 5     nReturn = 10

条件覆盖率100%

我们看到,上面三种覆盖率结果看起来都很酷!都达到了100%!主管可能会非常的开心,但是,让我们再去仔细的看看,上面被测代码中,nReturn的结果一共有四种可能的返回值:0,1,10,11,而我们上面的针对每种覆盖率设计的测试案例只覆盖了部分返回值,因此,可以说使用上面任一覆盖方式,虽然覆盖率达到了100%,但是并没有测试完全。接下来我们来看看针对路径覆盖设计出来的测试案例:

TestCase1 a = 5,    b = 5     nReturn = 0

TestCase2 a = 15,  b = 5     nReturn = 1

TestCase3 a = 5,    b = 15   nReturn = 10

TestCase4 a = 15,  b = 15   nReturn = 11

路径覆盖率100%

太棒了!路径覆盖将所有可能的返回值都测试到了。这也正是它被很多人认为是“最强的覆盖”的原因了。

还有一些其他的覆盖方式,如:循环覆盖(LoopCoverage),它度量是否对循环体执行了零次,一次和多余一次循环。剩下一些其他覆盖方式就不介绍了。

总结

通过上面的学习,我们再回头想想,覆盖率数据到底有多大意义。我总结了如下几个观点,欢迎大家讨论:

a. 覆盖率数据只能代表你测试过哪些代码,不能代表你是否测试好这些代码。(比如上面第一个除零Bug)

b. 不要过于相信覆盖率数据。

c. 不要只拿语句覆盖率(行覆盖率)来考核你的测试人员。

d. 路径覆盖率 > 判定覆盖 > 语句覆盖

e. 测试人员不能盲目追求代码覆盖率,而应该想办法设计更多更好的案例,哪怕多设计出来的案例对覆盖率一点影响也没有。


原文地址:http://blog.51cto.com/8132260/2136482

时间: 2024-10-11 04:25:50

在撩单元测试代码覆盖率的相关文章

MV*浏览器单元测试+代码覆盖率

好久没写BLOG了,最近弄了一个前端单元测试加代码覆盖率测试框架,使用起来非常简单,具体使用方法和介绍如下,先上图,结果如下: github地址:https://github.com/wf123537200/FeTestAndCov frontEnd-test-and-cov 这是一个便捷,快速的,浏览器友好的单元测试框架. 提供功能为: 1.提供实时前端单元测试结果. 2.提供单元测试覆盖率实时显示. 为什么有这个框架? 因为作为前端一直想实现tdd开发,之前受到几种限制: 1.使用jq的年代

单元测试-代码覆盖率工具 -- JaCoCo

最近学习Mybatis的官方文档,看到了[项目文档]一节有很多内容没有见过,做个笔记,理解一下. 随着敏捷开发的流行,编写单元测试已经成为业界共识.但如何来衡量单元测试的质量呢?有些管理者片面追求单元测试的数量,导致底下的开发人员投机取巧,编写出大量的重复测试,数量上去了,质量却依然原地踏步.相比单纯追求单元测试的数量,分析单元测试的代码覆盖率是一种更为可行的方式.JaCoCo(Java Code Coverage)就是一种分析单元测试覆盖率的工具,使用它运行单元测试后,可以给出代码中哪些部分被

使用JaCoCo统计单元测试代码覆盖率

1 JaCoCo介绍 JaCoCo是EclEmma团队基于多年覆盖率库使用经验总结而研发的一个开源的Java代码覆盖率库. 2 Maven单模块接入 在工程的pom.xml文件中添加如下内容: <build> <plugins> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>

python统计单元测试代码覆盖率

1.1.1实现过程 准备工作首先在运行命令前,我们需要安装一个包:coverage命令如下:pip install coverage #(py -3 -m pip install coverage)安装成功后,会出现Successfully等字样. 实现代码覆盖率命令一共涉及到的命令有3个,这里先全部给出,后续在用例子做详细使用讲解:第一个命令为运行一下你的测试脚本文件,第二个命令会在控制台打印出覆盖率报告信息,第三个命令会在同级目录下生成一个htmlcov的文件夹,打开文件夹中的index.h

3、学习单元测试代码覆盖率工具的使用(1)把一个英语句子中的单词次序颠倒后输出。

import java.util.Scanner; public class TestTest { public static void main(String [] a){ Scanner in=new Scanner(System.in); while(true){ String s=in.nextLine(); if(s.equalsIgnoreCase("quit")){ System.exit(0); } String[] ss=s.split("\\s+"

好代码是管出来的——.Net Core中的单元测试与代码覆盖率

测试对于软件来说,是保证其质量的一个重要过程,而测试又分为很多种,单元测试.集成测试.系统测试.压力测试等等,不同的测试的测试粒度和测试目标也不同,如单元测试关注每一行代码,集成测试关注的是多个模块是否能正常的协同工作. 当我们在衡量代码好坏时,其中一点就是这些代码是否进行了单元测试,测试的质量.代码覆盖率怎么样?本文将从以下几个方面介绍.Net Core中的单元测试: 单元测试简介 .Net Core中的单元测试框架 使用xUnit.Net对.Net Core应用进行单元测试 创建xUnit.

Android-jacoco代码覆盖率:单元测试覆盖率+功能测试覆盖率

参考:https://docs.gradle.org/current/dsl/org.gradle.testing.jacoco.tasks.JacocoCoverageVerification.html gradle库下载:https://maven.aliyun.com/mvn/view 案例参考来源:https://www.jianshu.com/p/1a4a81f09526 https://www.jianshu.com/p/1a4a81f09526 其他:https://testerh

实验二 单元测试

1. 学习单元测试和代码覆盖率工具的使用 (1)写一个程序,用于分析一个字符串中各个单词出现的频率,并将单词和它出现的频率输出显示.(单词之间用空格隔开,如“Hello World My First Unit Test”): (2)编写单元测试进行测试: (3)用ElcEmma查看代码覆盖率,要求覆盖率达到100%. import java.util.HashMap; import java.util.Map; public class Test { private static String

Visual Studio 2005 自带单元测试

一 单元测试简介 单元测试是代码正确性验证的最重要的工具,也是系统测试当中最重要的环节.也是唯一需要编写代码才能进行测试的一种测试方法.在标准的开发过程中,单元测试的代码与实际程序的代码具有同等的重要性.每一个单元测试,都是用来定向测试其所对应的一个单元的数据是否正确. 单元测试是由程序员自己来完成,最终受益的也是程序员自己.可以这么说,程序员有责任编写功能代码,同时也就有责任为自己的代码编写单元测试.执行单元测试,就是为了证明这段代码的行为和我们期望的一致. 单元测试还具有一下几个好处:  能