`
endual
  • 浏览: 3558530 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

单元测试代码覆盖率浅谈(转)

    博客分类:
  • java
 
阅读更多

单元测试代码覆盖率浅谈

 

在做单元测试时,代码覆盖率常常被拿来作为衡量测试好坏的指标,甚至,用代码覆盖率来考核测试任务完成情况,比如,代码覆盖率必须达到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)

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

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


设计判定覆盖案例时,我们只需要考虑判定结果为truefalse两种情况,因此,我们设计如下的案例就能达到判定覆盖率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


通过上面的例子,我们应该很清楚了判定覆盖和条件覆盖的区别。需要特别注意的是:条件覆盖不是将判定中的每个条件表达式的结果进行排列组合,而是只要每个条件表达式的结果truefalse测试到了就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的结果一共有四种可能的返回值:011011,而我们上面的针对每种覆盖率设计的测试案例只覆盖了部分返回值,因此,可以说使用上面任一覆盖方式,虽然覆盖率达到了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. 测试人员不能盲目追求代码覆盖率,而应该想办法设计更多更好的案例,哪怕多设计出来的案例对覆盖率一点影响也没有。

 

 

 

 

分享到:
评论

相关推荐

    代码覆盖率浅谈

    在做单元测试时,代码覆盖率常常被拿来作为衡量测试好坏的指标,甚至,用代码覆盖率来考核测试任务完成情况,比如,代码覆盖率必须达到80%或 90%。于是乎,测试人员费尽心思设计案例覆盖代码。用代码覆盖率来衡量...

    浅谈代码覆盖率

    经常有人问这样的问题:“我们在做单元测试,那测试覆盖率要到...  代码覆盖率的意义  分析未覆盖部分的代码,从而反推在前期测试设计是否充分,没有覆盖到的代码是否是测试设计的盲点,为什么没有考虑到?需求/设计

    智能运维:浅谈持续集成( CI)、持续交付(CD) 和软件测试.pdf

    测试自动化是CI/CD成功的关键,它减少了手动测试的时间和错误,提高了测试覆盖率和效率。通过自动化测试,可以快速验证软件的功能和性能,确保每次发布的软件都是经过严格验证的。 在互联网行业中,CI/CD的广泛应用...

    phpunit+selenium测试环境搭建浅谈

    此外,使用代码覆盖率工具(如`php-code-coverage`)可以帮助我们了解测试覆盖的代码范围,以便进一步完善测试。 总的来说,`phpunit`和`selenium`的组合为PHP开发者提供了一个强大的测试工具链,能够有效地提升...

    浅谈Angular单元测试总结

    白盒测试则需要了解程序内部结构和逻辑,常用于代码覆盖率分析等。 其他测试类型包括回归测试,确保之前通过的测试在软件变更后依然通过;冒烟测试,确保新版本的软件基本功能能够正常运行;随机测试,即输入随机...

    智能运维:浅谈持续集成( CI)、持续交付(CD) 和软件测试.docx

    ### 智能运维:浅谈持续集成(CI)、持续交付(CD)与软件测试 #### 一、持续集成(CI) 持续集成(Continuous Integration,简称CI)是一种软件开发实践,它要求团队成员频繁地将他们的代码变更合并到一个共享的主...

    论软件测试方法和工具的选择

    这种方法有效地提高了测试覆盖率,确保了软件功能的完整性。 #### 压力测试 考虑到CAPP系统采用了客户端/服务器(C/S)架构,在实际应用中可能会遇到高并发请求的情况,项目组选择了LoadRunner来进行压力测试。...

    软件测试必看 入门级的教程

    8.3.3 必须制订覆盖率指标和质量目标来指导和验收单元测试 38 8.3.4 加强详细设计文档评审 39 8.4 单元测试者技能的提高 39 8.4.1 加强对单元测试人员的技能培训 39 8.4.2 必须引入工具进行辅助 40 8.4.3...

    软件测试方法案例设计的论文

    此外,论文可能还会探讨如何评估测试的有效性和效率,如覆盖率指标(如语句覆盖、分支覆盖)以及测试优化策略,如优先级排序和回归测试管理。测试过程中的问题定位和缺陷管理也是重要的内容,包括如何记录和跟踪缺陷...

    浅谈软件开发流程.pdf

    《浅谈软件开发流程》 软件开发是一项复杂而系统的过程,旨在通过一系列有序的步骤,将用户的需求转化为可用的软件产品。本文将围绕软件开发流程的各个环节进行深入探讨,旨在提供一套适用于中小型软件项目开发的...

    浅谈软件开发过程中的软件质量保证.pdf

    代码审查可以帮助开发者发现并修正语法错误、逻辑漏洞等问题,而单元测试则确保每个模块的功能正确无误。 #### 4. 测试阶段的质量保证 测试阶段是软件质量保证的核心环节。它包括功能测试、性能测试、安全测试、...

    mock

    在Struts2中,Mocking可以帮助我们分离Action的测试和依赖服务的实现,提高测试的效率和覆盖率。通过模拟HTTP请求、Session和SessionFactory等,我们可以创建一个可控的测试环境,确保Action在不同场景下的行为都...

    浅谈:什么是数据驱动编程的详解

    4. **可测试性**:数据驱动的设计有助于提高单元测试的覆盖率,因为可以通过提供不同的数据集来测试程序的不同行为。 #### 实践案例分析 为了更好地理解数据驱动编程的实际应用场景,我们来看一个具体的案例——...

Global site tag (gtag.js) - Google Analytics