片面追求单元测试覆盖率之我见

格兰特杨 2010-06-12 06:44:14
一些个人之见,讲出来希望大家讨论。这完全是工作中遇到的一些具体问题抽象出来的一个东西,仅属于个人之见,任何的意见都是欢迎的。

----------------------------------------------------------------------
在工作中,当被问到“如何提高代码质量”,回答无非如下几个,增加评审,代码规约,单元测试。不知起自何年何月,如今一些机构开始引入“单元测试覆盖率”的概念,并由此对程序员提出了覆盖率要达到70%,90%,以此来评判程序员工作的质量,以及产品的质量。这里先预为单元测试下定义以免混淆,即,基于Junit,类与代码级别的,与运行时无关的白盒测试。

而单元测试覆盖率,何以得知呢?一个(当然不是所有)最常使用的办法便是如cobertura之类的工具来执行Junit的测试用例,这些工具不会去统计程序的逻辑,而是判断用例执行到的行数。如果再引入hudson一类的持续集成工具,便可生成漂亮的日报表,管理层也就可能直接地了解这些技术数据。这些都是好的实践,但我想说的并非这些,而是想与大家探讨这些数据是否与产品的质量有着必然的相关。

写过测试用例的朋友们一般地会了解,测试用例与接口有必然的关联,而多数情况下会是直接调用。修改接口会带来测试用例的修改,这是多数情况下不争的事实。有人说为测试可以预留专用的接口,但这种做法在一个要求代码安全的机构中不会得到接受。所以,代码评审/重构或代码上下文变更导致的接口变化,多数情况下会带来测试用例的修改;用例越丰富,这部分的工作量越大,此其一。

其二,讨论单元测试覆盖率与正确代码逻辑的相关性。我们需要衡量的代码的逻辑,乃是在运行时外部触发系统时可能运行的所有逻辑,其包含接口内部逻辑,也包含接口调用逻辑。当接口间存在内容耦合和控制耦合时,合理的测试用例中对于接口间的调用顺序和想定上下文都有要求,与覆盖率并不相关。如a-b-c与c-b-a的调用,覆盖率完全相同,但结果可能完全不同。这一点在测试状态机相关的代码时显得更为重要。

其三,我们经常说到20%和80%的关系。即有80%的时间应当用在20%重要性的事情上。反观程序员写的代码,关键模块与非关键模块的比例也如是。倘时间不是问题自然可以把所有的事情做到完美,一旦时间是问题,哪一个优先,自一目了然。问题是,关键模块由于逻辑复杂,依赖较多,往往是最难测试,其用例也最难写作。人性往往倾向于先易后难。若覆盖率是唯一的指标,最关键的部分便很容易因为覆盖率目标已经达到而被忽略,80%的覆盖恰恰没有覆盖的是最应该测试的代码,很不幸,最易测试的,是那些对于POJO,数据库,配置文件操作的清晰逻辑,通过一个集成测试的用例就可以全部保证的。

那末,为什么覆盖率会成为这样多项目经理趋之若鹜的管理方法呢?显然,这是一个节约管理成本可见收效的举措。只需要把cobertura和hudson配好了放在那里并说明给所有程序员,就可以坐看数字一天一天的上升而觉得质量在一天一天地变好,不需要组织什么评审,不需要去过问每一个人的状况,不需要再去费力地去看报告,何乐而不为?

容我说,懂得我上面说的话,却坚持还要这样做的人可以被看成软件项目管理中的官僚主义。人力就是人力,它不会凭空变少,也不会凭空变多。用在70%-90%覆盖率上的人力成本,与其他方法比起来值与不值?软件工程没有银弹,只有综合各种方法形成最适合自己产品的方法才是最好的质量控制。在我个人看来,就单元测试而言,确定我们拥有高质量的测试用例,确定关键的模块都有相关的测试对应,比简单地追求覆盖率要重要得多。而这些通过传统的评审和走查可以轻易做到的事情,却常常被我们定义流程和日常管理中忽略了。
...全文
1100 30 打赏 收藏 转发到动态 举报
写回复
用AI写文章
30 条回复
切换为时间正序
请发表友善的回复…
发表回复
andycode 2011-03-20
  • 打赏
  • 举报
回复
麻烦介绍几本单元测试书籍。
格兰特杨 2010-06-22
  • 打赏
  • 举报
回复
@jason_dct:

我个人认为开发人员的测试在测试驱动的流程中更加有意义。传统的瀑布流程并非一无是处,对于需求稳定的系统它能够做出更加坚实的架构和更加明确的里程碑。在瀑布流程中,开发人员也许会更多关注自动化的集成测试;而敏捷/XP中,测试在很早就被引入,而此时并不具备完整集成测试的条件,也许这也是我们也经常叫这类测试为“单元测试”的原因之一。其实,个人的理解这些流程并无规定测试的具体方法。若是测试框架比较完整,在敏捷中使用持续集成测试也完全可能。
格兰特杨 2010-06-22
  • 打赏
  • 举报
回复
@sp1234:

看得出您在XP方面有相当多的经验。如果您有时间的话,我想和您探讨一下,在您基于XP开发的项目中,如何保证测试用例的质量的,以及怎样平衡解决重构带来的成本与时间表的冲突?谢谢。

格兰特杨 2010-06-22
  • 打赏
  • 举报
回复
@ericzhangali: 呵呵。我只是表达观点,并不想吵架。所以有些贴子我回,有些我不是很想回复。我想你看了有些回贴也能够理解。

@sp1234: 相信我们的大部分观点是一致的,握手!对于您的"我的书架上就有至少2本十几年前的、我认为现在也很难找到相当水平的关于传统自动化测试书籍,可是它们毕竟都是过时的、有害的书籍,虽然写得太好、那些理论太能用来忽悠人了。"这句话,我深表赞同。

@microtry:
这里,再次深感把概念统一是进行深入讨论的基础。对于“单元测试”这四个字如果大家还没有统一的认知,则很多讨论就是自我自话,鸡同鸭讲。其实我一再强调我说的是基于xUnit的狭义的单元测试,可能很多人还没有了解。再说一遍,我反对的就是覆盖率即质量本身这样的论断,而且请万万不要把“追求质量"和“追求覆盖率”相提并论,这是有害的,而且会误导大量的程序员和项目经理。

1. 用工具统计出来的“覆盖率”,是执行xUnit测试时走过的代码率,是不是与书本上讲的“覆盖率”是一回事,值得讨论,至少不能代表逻辑覆盖率。
2. 有限的人力资源,是否需要优先用在更有针对性的测试上,而不是盲目要求一个覆盖率的百分比。覆盖率百分比高,并不能代表最重要的部分已经被覆盖。至于说到100%覆盖率,有一句话要奉送,纸上谈兵不如退而结网。
3. 有很多其他有效的方式可以控制质量,单元测试只是其中之一,而且不是万能的。如@sp1234所讨论的,在XP的流程中,单元测试用来驱动开发,关键的持续质量提升在于持续的重构。
4. 我无意就一些浮在空中的理论做一些漫无边际的探讨;在某本书上抄一些理论贴在这里,并做一些想当然却似是而非的论断是很容易的,把这些论断用在生产中则是另外一回事。所以您的第2,3,4条,请允许我不做评论。
loveisbug 2010-06-20
  • 打赏
  • 举报
回复
看楼主怎么掰回去。
小_虎 2010-06-19
  • 打赏
  • 举报
回复
买了本《.Net自动化测试之道》,一直没看,刚买回来,就忙死了。
  • 打赏
  • 举报
回复
TDD需要非常好的编写代码能力,和比较清晰的头脑,是对编程技术的高要求。国内的手工测试人员,基本上不胜任。
段传涛 2010-06-19
  • 打赏
  • 举报
回复
我也在学习研究,看了一下博客园的文章,现在好像项目经理比较喜欢这个,我个人认为程序员测试自己的代码好像不太合适的。
况且 专业的测试人员可以很好的搞定这个问题啊。
  • 打赏
  • 举报
回复
在写实现程序之前先写测试然后才是先 --> 在写实现程序之前先写测试然后才实现

传统的单元测试确实额外地写了很多没有什么价值的测试。当一个程序员都写好了实现代码,它立刻写测试来证明自己有错误吗?这是不可能的。它只能写一些将垃圾固定下来当作教条的测试代码,而不是尽可能为系统重构留下灵活性。

而TDD是系统重构的关键保证,是“安全网”。在敏捷开发中最重要的重点之一就是强调开发时只考虑最简单的二三两个属性或者一两个方法的测试,当许多TDD将系统的框架驱动完成时,就重构!通过重构来提高系统性能、改进系统扩展性。如果写传统的单元测试,只在低级的地方给自己设置教条,而在高级的地方没有写下测试代码,与TDD的实际作用正好相反。
  • 打赏
  • 举报
回复
Kent Beck所发明的xUnit系列,是对之前几十年前的自动测试程序极大的简化,仅仅保留了最少、最必要的一点内容(可能最近三、四年才逐渐被变得更加臃肿和庸俗)。因此,如果第一次接触xUnit工具,就掉进传统的单元测试理论里边去了,就是对xUnit系列工具的初衷开了个国际大玩笑。

可是,仅仅抄袭网上的关于单元测试的文章,对许多沽名钓誉的人是个捷径,从来不去研究软件工程以及xUnit工具的背后的历史。单元测试的覆盖率概念,有至少30年的历史,我的书架上就有至少2本十几年前的、我认为现在也很难找到相当水平的关于传统自动化测试书籍,可是它们毕竟都是过时的、有害的书籍,虽然写得太好、那些理论太能用来忽悠人了。
  • 打赏
  • 举报
回复
对于XP开发来说,TDD要求语句的原则是:如果删除一些语句仍然可以让TDD通过,那么这些语句就是多余的。

因此TDD是从必要性这个角度来说测试覆盖率的,是从如何让程序员对测试驱动开发技术入门角度来谈的。注意,测试驱动开发不是时候测试,恰恰相反,它要求程序员习惯用测试来表达自己为什么要写程序,在写实现程序之前先写测试然后才是先(而不是相反)。

因此对于XP开发来说,TDD不是传统的测试。虽然使用单元测试工具,但是它根本不是传统的单元测试。
缪军 2010-06-18
  • 打赏
  • 举报
回复
1、覆盖率:我12楼已经阐述了我的观点;我可以再强调一遍:
物理覆盖100%是没商量的,
逻辑覆盖尽可能接近100%(实际上,许多团队只做到10%,这可能已经很卓越了),
如果逻辑覆盖能达到100%,就没有不合格产品了,
这个准则是传统工业已经确立的,在工厂里,没听说过“片面追求质量”这种说法
2、针对你的观点,你在17楼再次提到“如果这意味着API的改动,自然需要重写单元测试用例”,
正如很多人认为数据库修改,要去修改代码是天经地义的一样,
这些类似的观点都是我一直以来反对的;
3、你对测试的误解恰恰是很多人对测试的最大误解:测试要消耗大量的人力成本,
你见过哪些工厂是靠人工完成测试的?就连测试仪器都不是手工制造的!
4、测试是软件生产能力达到一定成熟阶段的必然需求和必然要解决的问题,
所以测试基本是是自动化的: 无论是单元测试还是UI测试;
格兰特杨 2010-06-18
  • 打赏
  • 举报
回复
看来该讨论已经偏离主题,不再是讨论覆盖率,而是测试方法与开发方法。不过仍然愿意做进一步交流。
--------------------------------------
@microtry: 我不会认为文档就是简单文字的东西,不过在我看来“文档即项目模型,整个开发的策动源”这个描述对我来讲有些抽象,我试图去理解您话后面的意思,看起来有点象是瀑布流程,不过自顶向下的文档驱动与逐步求精的测试驱动开发都是当前业界被接受以及被应用的流程,在我的工作经验中也都有遇到。我目前的理解是只有最适合的流程,没有哪个是放之四海而皆准的。

您说:如果你有10000个受测对象,你难道要求开发人员写10000个测试吗? 你不觉得是重复劳动吗?如果源代码改了呢?岂不是更加令人崩溃?!

首先,这10000个受测对象是如何定义的呢?假定我们讨论的是狭义单元测试中的代码模块,每一个模块的场景与应用上下文都不相同,10000个测试也是应当的。当然,要抽象出他们共同的部分,设计合理的用例,根据模块的复杂度合理规划用例的优先级,以避免你提到的重复劳动。我不太明白您的“源代码改了”意味着什么,如果这意味着API的改动,自然需要重写单元测试用例。这也是我在第一个贴子中提到的"代码评审/重构或代码上下文变更导致的接口变化,多数情况下会带来测试用例的修改;用例越丰富,这部分的工作量越大"。

如果我们讨论的是运行期相关的集成测试,则情况有所不同。很有可能,一个简单的集成测试可能覆盖七成以上的代码模块;但这并不意味着我们就不需要对复杂模块进行单独的代码级的单元测试。想说的是,单元测试与集成测试是相辅相成的关系,正如CMM2CMMI所说,针对不同类型的程序应赋予的权重不同。

最后,据我的理解其实我们说的完全不是同一件事情,您在强调某种流程下文档与集成测试的关系和重要性,而我的贴子在讨论片面追求单元测试覆盖率是否有益。希望您把我的贴子从头读一下,这样也许可以避免重新澄清很多定义上的问题。有概念上的共识才可以开展更有意义的讨论。
缪军 2010-06-18
  • 打赏
  • 举报
回复
如果你有10000个受测对象,你难道要求开发人员写10000个测试吗?
你不觉得是重复劳动吗?如果源代码改了呢?岂不是更加令人崩溃?!

单元测试,测试的是什么?测试的是产品是否达到设计要求,这个标准就是文档,
我所指的文档并不是你所理解的文字的东西,而是项目模型,是整个开发的策动源,
设计人员描述好文档后,文档就能够策动或生成绝大部分的应用程序代码和测试代码,

开发是由文档驱动的,而不是测试,
测试代码和应用程序代码只不过是文档的2个副本,是对同一种应用的2种描述,
一个是实施,一个是观测



格兰特杨 2010-06-18
  • 打赏
  • 举报
回复
@CMM2CMMI: 谢谢理解,这也是我发这篇文章想要让大家达成的共识。:)

@microtry: 您说到:""修改接口会带来测试用例的修改,这是多数情况下不争的事实",遗憾的是这是很多从事测试工作的开发人员的观点或实际处境,其实这是由于不正确的测试方法造成的"

如我们所讨论,测试方法有很多种。这里讨论的,是狭义的单元测试。即,基于Junit或某种类似的工具,类与代码级别的,与运行时无关的白盒测试。在这里我们讨论的接口,是编程接口即事实可调用的API,而非项目文档的描述。测试代码依赖项目文档的情况当然也有,但应该多数是用户行为或信令驱动的集成测试用例,而这样的用例是无法或很难使用工具来统计覆盖率的 - 即使可以,也已经与狭义的单元测试覆盖率不能做为相同的指标。虽然我个人认这集成测试用例在多数情况下更为有效,却还不敢说狭义的单元测试是不正确的测试方法。:) 欢迎继续讨论!

cmm2cmmi 2010-06-17
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 davy_chen 的回复:]
做能够做到的(代码覆盖率能够让多数人做到并被别人看到),片面追求代码覆盖率固然不好,但是单就代码覆盖率本身并没有什么错误。刀其实是中性的,但是到了歹徒手中就成了凶器。测试中各种方法、工具、理论总体来说是好的,但是如果让人错误的使用,就会出现非期望的结果。
[/Quote]

刀论
支持^^
davy_chen 2010-06-17
  • 打赏
  • 举报
回复
做能够做到的(代码覆盖率能够让多数人做到并被别人看到),片面追求代码覆盖率固然不好,但是单就代码覆盖率本身并没有什么错误。刀其实是中性的,但是到了歹徒手中就成了凶器。测试中各种方法、工具、理论总体来说是好的,但是如果让人错误的使用,就会出现非期望的结果。
缪军 2010-06-17
  • 打赏
  • 举报
回复
我在学习测试的知识的时候,很自然的就把OO的思想融入进来,
你们把接口搞错对象了,接口是项目文档
AppCode、TestCode、DB都遵从一个接口
缪军 2010-06-17
  • 打赏
  • 举报
回复
2、"修改接口会带来测试用例的修改,这是多数情况下不争的事实",遗憾的是这是很多从事测试工作的开发人员的观点或实际处境,其实这是由于不正确的测试方法造成的,
测试代码依赖的是项目文档,而不是被测代码,
测试代码的改变只有一个理由:就是项目文档改变了,
所谓单元测试自动化,不是根据被测单元自动生成代码,而是项目文档有能力自动生成测试代码,或者干脆项目文档就具备测试能力,
在整个开发中,项目文档驱动了一切:Code、TestCode、DB
缪军 2010-06-17
  • 打赏
  • 举报
回复
我学习测试方面的知识至今不足2月,但是我发现以我找到的资料看,
国内关于软件测试的水平如同软件生产一样落后,就单元测试而言:
1、覆盖率分:逻辑覆盖和物理覆盖,以现有的手段物理覆盖100%是没问题的,
逻辑覆盖很难达到100%,但这是衡量测试手段的重要指标,这个覆盖率是动态的,
也就是说你的测试工具应当允许随时增加测试数据甚至测试用例以满足覆盖要求,
应该说:任何质量反馈都可能促使我们增加覆盖;

接楼下
加载更多回复(9)

5,227

社区成员

发帖
与我相关
我的任务
社区描述
软件工程/管理 质量管理/软件测试
功能测试压力测试安全性测试 个人社区 湖南省·长沙市
社区管理员
  • 软件测试
  • 虫无涯
  • 小博测试成长之路
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

欢迎大家加入到软件测试的社区,在这里,希望大家勇于发表自己的看法,欢迎大家分享自己在软件测试工作过程中遇到的问题以及工作经验分享。

1.想转行的小伙伴,遇到问题没有及时回复的,可以私聊小博进行反馈

2.大家对社区有好的建议,都可以在社区发帖进行反馈

推荐大家学习的软件测试入门笔记:软件测试入门学习笔记

试试用AI创作助手写篇文章吧