都100%代码覆盖了,还会有什么问题?

(图片来自:http://t.cn/R06rQHi)

引言

很多人看到这个标题时,都会想“你都100%代码覆盖了,怎么还会有问题呢?”

让我们看一下代码例子:

public class TestCalculator {

  public Double add(Double a, Double b) {

    return a + b;}

}

再看看用junit写出的测试代码:

@Test
public void testAdd() {

  Double a = new Double(1);
       
  Double b = new Double(2);
       
  Double c = new Double(3);
       
  assertEquals(c, testCalculator.add(a, b));

}

当我们使用EclEmma或者Jacoco来进行覆盖测试时,对于这个类,我们将得到100%测试覆盖率。

一切看起来都那么的完美,真是这样的吗?

好吧,让我们来来看看另一个测试,当其中一个变量为null时,返回值将会是什么?

@Test
public void testAddNullPointerException() {

  Double a = new Double(1);

  Double b = null;

  Double c = new Double(3);

  assertEquals(c, testCalculator.add(a, b));

}

好了,你会发现尽管覆盖率为100%,但程序却抛出了NullPointerException。

那么肯定有人会问,这样的话单元测试覆盖率的高低都不能作为衡量项目代码质量的指标,那么我们要单元测试还有什么用?

首先,我想我们可能搞错了测试覆盖的定义。

我们先听听Martin Fowler对于测试覆盖的定义:

Test coverage is a useful tool for finding untested parts of a codebase. Test coverage is of little use as a numeric statement of how good your tests are.

(图片来自:http://t.cn/R06jK5U)

他认为:把测试覆盖作为质量目标没有任何意义,我们应该把它作为一种发现未被测试覆盖的代码的手段。


所以100%的代码覆盖率还值得追求吗?

当然,这应该是每个程序员毕生的追求之一,但是如果从项目角度考虑ROI(投入产出比),对于需要快速上线的短期项目,需要注重的是让测试覆盖核心功能代码。如果你的项目是一个长期项目,那么高覆盖率是非常有必要的,它意味着高可维护性,以及更少的bug。(前提是你的测试采用TDD/BDD方式编写,我见过将测试代码写的一团糟的人,看着他的代码,我宁愿重新写一遍。)

那么对于一个项目来说,覆盖率应该达到多少?

其实没有适用于所有项目的数值,每个项目都应有自己的阈值,但共性是,测试必须覆盖主要业务场景,代码的逻辑分支也必须尽可能的覆盖。

如何改进你的项目代码覆盖率?

首先我们要阅读和理解项目代码,找出其中需要测试并且与业务强相关的代码,结合sonar等代码质量管理平台,从代码编写规范、复杂度、重复代码等方面进行代码重构,进一步提高项目的可维护性与可读性。

这也意味着重构,重构的同时,你需要更多的测试来保证你重构代码的正确性。

其次要对code coverage进行度量分析,那么我们应该怎么度量code coverage?

一般来说我们从以下四个维度来度量,如上图所示:

  • 行覆盖率(line coverage):度量被测代码中每个可执行语句是否都被执行到,但不包括java import,空行,注释等。
  • 函数覆盖率(function coverage):度量被测代码中每个定义的函数是否都被调用。
  • 分支覆盖率(branch coverage):度量被测代码中每一个判定的分支是否都被测试到。
  • 语句覆盖率(statement coverage):度量被测代码是否每个语句都被执行。

所以行覆盖率的高低不能说明项目的好坏,我们要从多方面进行思考,一般我们遵循的标准应是:函数覆盖率 > 分支覆盖率 > 语句覆盖率

代码覆盖率最重要的意义在于:

  • 阅读分析之前项目中未覆盖部分的代码,进而反推在前期QA以及相关测试人员在进行黑盒测试设计时是否考虑充分,没有覆盖到的代码是否是测试设计的盲点,为什么没有考虑到?是需求或者UX设计不够清晰,还是测试设计的理解有误。
  • 检测出程序中的废代码,可以逆向反推代码设计中不合理的地方,提醒设计/开发人员理清代码逻辑关系,提升代码质量。
  • 代码覆盖率高不能说明代码质量高,但是反过来看,代码覆盖率低,代码质量绝对不会高到哪里去,可以作为测试自我审视的重要工具之一。

结束语

单元测试的覆盖率并不只是为了取悦客户或者管理层的数据,它能够实实在在反应项目中代码的健康程度,帮助我们更好的改善了代码的质量,增加了我们对所编写代码的信心。


原文发布于微信公众号 - 思特沃克(ThoughtWorks)

原文发表时间:2017-09-26

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏葡萄城控件技术团队

当心那些有歧义的命名

关键点 “别人还能把这个名字理解成什么意思?”通过不断的问自己这个问题来积极检查每一个命名。 事实上,这种富有创造性的、不断尝试“错误理解”的方法,能够有...

2086
来自专栏机器之心

放弃Python转向Go语言:我们找到了以下9大理由

选自Stream 作者:Thierry Schellenbach 机器之心编译 参与:黄小天、李亚洲 转用一门新语言通常是一项大决策,尤其是当你的团队成员中只有...

60711
来自专栏顶级程序员

Java 9、10、11,谁才是Java程序员的本命?

之前,我们在《Java 10无跳票发布,主推的新特性引争议》的文章中做了一个小的调查,主要是调查现在的Java程序员都在使用哪个版本的Java?根据调查结果,绝...

943
来自专栏Golang语言社区

Go 的垃圾回收机制在实践中有哪些需要注意的地方?

之前回答问题的时候Go还处在1.1版本,到了1.2和1.3,Go的GC跟踪命令和GC内部实现已经有一些变化,并且根据评论中的反馈,这边一并做补充说明。 Go ...

4916
来自专栏HappenLee的技术杂谈

C++雾中风景12:聊聊C++中的Mutex,以及拯救生产力的Boost

C++从11开始在标准库之中引入了线程库来进行多线程编程,在之前的版本需要依托操作系统本身提供的线程库来进行多线程的编程。(其实本身就是在标准库之上对底层的操作...

973
来自专栏数据派THU

放弃Python转向Go语言:这9大理由就够了 !(附代码)

来源:机器之心 作者:Thierry Schellenbach 本文为你介绍八个短时间可以完成的趣味机器学习项目。 转用一门新语言通常是一项大决策,尤其是当你的...

55112
来自专栏java思维导图

程序员,请优先提高代码的可读性

现在,当有人提及“优化”一词时,他们通常是指“优化执行时间”,除非他们明确表明要优化GPU的内存消耗,网络流量等等。

954
来自专栏陈树义

JVM系列第1讲:Java 语言的前世今生

Java 语言是一门存在了 20 多年的语言,其年纪比我自己还大。虽然存在了这么长时间,但 Java 至今都是最大的工业级语言,许多大型互联网公司均采用 Jav...

1143
来自专栏AlgorithmDog的专栏

Akka 使用系列之二: 测试

通过上一篇文章,我们已经大致了解怎么使用 Akka,期待细致用法。这篇文章将介绍如何用 Akka-testkit 对 Akka 程序进行测试。 ? ...

2057

为什么我们从Python切换到Go?

切换到新的编程语言向来是关键一步,尤其是当你的团队只有一位成员有该语言的使用经验时。今年年初,我们将 Stream 的主要编程语言从Python 切换到 Go。...

2972

扫码关注云+社区

领取腾讯云代金券