Cobertura 是一U开源工P它通过基本的代码Qƈ观察在测试包q行时执行了哪些代码和没有执行哪些代码,来测量测试覆盖率。除了找出未试到的代码q发?bug 外,Cobertura q可以通过标记无用的、执行不到的代码来优化代码,q可以提?API 实际操作的内部信息。Elliotte Rusty Harold 与您分享如何利用代码覆盖率的最佛_跉|使用 Cobertura?/BLOCKQUOTE>
管试先行~程Qtest-first programmingQ和单元试已不能算是新概念Q但试驱动的开发仍然是q去 10 q中最重要的编E创新。最好的一些编Eh员在q去半个世纪中一直在使用q些技术,不过Q只是在最q几q_q些技术才被广泛地视ؓ在时间及成本预算内开发健壮的无缺陯Y件的关键所在。但是,试驱动的开发不能超q测试所能达到的E度。测试改q了代码质量Q但q也只是针对实际试到的那部分代码而言的。您需要有一个工具告诉您E序的哪些部分没有测试到Q这样就可以针对q些部分~写试代码q找出更?bug?
Mark Doliner ?Cobertura Q?I>cobertura 在西班牙语是覆盖的意思)是完成这Q务的一个免?GPL 工具。Cobertura 通过用额外的语句记录在执行测试包Ӟ哪些行被试到、哪些行没有被测试到Q通过q种方式来度量字节码Q以便对试q行监视。然后它生成一?HTML 或?XML 格式的报告,指出代码中的哪些包、哪些类、哪些方法和哪些行没有测试到。可以针对这些特定的区域~写更多的测试代码,以发现所有隐藏的 bug?
阅读 Cobertura 输出
我们首先查看生成?Cobertura 输出。图 1 昄了对 Jaxen 试包运?Cobertura 生成的报告(请参?参考资?/FONT>Q。从该报告中Q可以看C很好Q在 org.jaxen.expr.iter
包中几乎?100%Q到极差Q在 org.jaxen.dom.html
中完全没有覆盖)的覆盖率l果?/P>
?1. Jaxen 的包U别覆盖率统计数?/B>
Cobertura 通过被测试的行数和被试的分支数来计覆盖率。第一ơ测试时Q两U测试方法之间的差别q不是很重要。Cobertura qؓc计^?McCabe 复杂度(请参?参考资?/FONT>Q?/P>
可以深入挖掘 HTML 报告Q了解特定包或者类的覆盖率。图 2 昄?org.jaxen.function
包的覆盖率统计。在q个包中Q覆盖率的范围从 SumFunction
cȝ 100% ?IdFunction
cȝ仅ؓ 5%?/P>
?2. org.jaxen.function 包中的代码覆盖率
q一步深入到单独的类中,具体查看哪一行代码没有测试到。图 3 昄?NameFunction
cM的部分覆盖率。最左边一栏显C受后一栏显CZ执行试时这一行被执行的次数。可以看出,W?112 行被执行?100 ơ,W?114 行被执行?28 ơ。用U色H出昄的那些行则根本没有测试到。这个报告表明,虽然从M上说该方法被试CQ但实际上还有许多分支没有测试到?
?3. NameFunction cM的代码覆盖率
 |
Cobertura ?jcoverage 的分支(请参?参考资?/FONT>Q。GPL 版本?jcoverage 已经有一q没有更新过了,q且有一些长期存在的 bugQCobertura 修复了这?bug。原来的那些 jcoverage 开发h员不再l开发开放源码,他们转向开?jcoverage 的商业版?jcoverage+Qjcoverage+ 是一个从同一代码基础中发展出来的闭源代码品。开放源码的奇妙之处在于Q一个品不会因为原开发h员决定让他们的工作获得相应的报酬而消亡? | |
认遗漏的测?/FONT>
利用 Cobertura 报告Q可以找Z码中未测试的部分q对它们编写测试。例如,?3 昄 Jaxen 需要进行一些测试,q用 name()
函数Ҏ字节炏V注释节炏V处理指令节炏V属性节点和名称I间节点q行试?
如果有许多未覆盖的代码,?Cobertura 在这里报告的那样Q那么添加所有缺的试会非常耗时Q但也是值得的。不一定要一ơ完成它。您可以从被试的最的代码开始,比如那些所有没有覆盖的包。在试所有的包之后,可以对每一个显CZؓ没有覆盖的类~写一些测试代码。对所有类q行专门试后,q要为所有未覆盖的方法编写测试代码。在试所有方法之后,可以开始分析对未测试的语句q行试的必要性?
Q几乎)不留下Q何未试的代?/FONT>
是否有一些可以测试但不应试的内容?q取决于您问的是谁。在 JUnit FAQ 中,J. B. Rainsberger 写到“一般的看法是:如果 自n 不会出问题,那么它会因ؓ太简单而不会出问题。第一个例子是 getX()
Ҏ。假?getX()
Ҏ只提供某一实例变量的倹{在q种情况下,除非~译器或者解释器Z问题Q否?getX()
是不会出问题的。因此,不用试 getX()
Q测试它不会带来M好处。对?setX()
Ҏ来说也是如此Q不q,如果 setX()
Ҏ实要进行Q何参数验证,或者说实有副作用Q那么还是有必要对其q行试。?
 |
理论上,Ҏ覆盖的代码编写测试代码不一定就会发?bug。但在实践中Q我从来没有到没有发现 bug 的情c未试的代码充满了 bug。所做的试少Q在代码中隐藏的、未发现?bug ׃多?/P> | |
我不同意。我已经C清在“简单得不会出问题”的代码中发现的 bug 的数量了。确实,一?getter ?setter 很简单,不可能出问题。但是我从来没有办法区分哪些方法是真的单得不会出错Q哪些方法只是看上去如此。编写覆盖像 setter ?getter q样单方法的试代码q不难。ؓ此所q量旉会因为在q些Ҏ中发现未N料到?bug 而得到补ѝ?
一般来_开始测量后Q达?90% 的测试覆盖率是很Ҏ的。将覆盖率提高到 95% 或者更高就需要动一下脑{。例如,可能需要装载不同版本的支持库,以测试没有在所有版本的库中出现?bug。或者需要重新构Z码,以便试通常执行不到的部分代码。可以对c进行扩展,让它们的受保护方法变为公共方法,q样可以对q些Ҏq行试。这些技巧看h像是多此一举,但是它们曑ָ助我在一半的旉内发现更多的未发现的 bug?
q不L可以得到完美的?00% 的代码覆盖率。有时您会发玎ͼ不管对代码如何改造,仍然有一些行、方法、甚x整个cL试不到的。下面是您可能会遇到的挑战的一些例子:
- 只在特定q_上执行的代码。例如,在一个设计良好的 GUI 应用E序中,d一?Exit 菜单的代码可以?Windows PC 上运行,但它不能?Mac Zq行?BR>
- 捕获不会发生的异常的
catch
语句Q比如在?ByteArrayInputStream
q行d操作时抛出的 IOException
?BR>
- 非公q中的一些方法,它们永远也不会被实际调用Q只是ؓ了满x个接口契U而必d现?BR>
- 处理虚拟?bug 的代码块Q比如说Q不能识?UTF-8 ~码?
考虑C面这些以及类似的情况Q我认ؓ一些极限程序员自动删除所有未试代码的做法是不切实际的,q且可能h一定的讽刺性。不能L获得l对完美的测试覆盖率q不意味着׃会有更好的覆盖率?
然而,比执行不到的语句和方法更常见的是D留代码Q它不再有Q何作用,q且从代码基中去掉这些代码也不会产生M影响。有时可以通过使用反射来访问私有成员这L怪招来测试未试的代码。还可以为未试的、包保护Qpackage-protectedQ的代码来编写测试代码,测试类攑ֈ要试的类所在那个包中。但最好不要这样做。所有不能通过发布的(公共的和受保护的Q接口访问的代码都应删除。执行不到的代码不应当成Z码基的一部分。代码基小Q它pҎ被理解和l护?
 |
不要漏掉量单元试包和cLw。我不止一ơ注意到Q某些个试Ҏ或者类没有被测试包真正q行。通常q表明名U规范中存在问题Q比如将一个方法命名ؓ tesSomeReallyComplexCondition Q而不是将其命名ؓ testSomeReallyComplexCondition Q,或者忘记将一个类dC suite() Ҏ中。在其他情况下,未预期的条gD跌了测试方法中的代码。不是什么情况,都是虽然已经~写了测试代码,但没有真正运行它。JUnit 不会告诉您它没有像您所想的那样q行所有测试,但是 Cobertura 会告诉您。找Z未运行的试后,Ҏ它一般很Ҏ? | |
q行 Cobertura
在了解了量代码覆盖率的好处后,让我们再来讨Z下如何用 Cobertura 量代码覆盖率的具体l节。Cobertura 被设计成为在 Ant 中运行。现在还没有q方面的 IDE 插g可用Q不q一两年内也许就会有了?
首先需要在 build.xml 文g中添加一个Q务定义。以下这个顶U?taskdef
元素?cobertura.jar 文g限定在当前工作目录中Q?
<taskdef classpath="cobertura.jar" resource="tasks.properties" />
|
然后Q需要一?cobertura-instrument
dQ该d在已经~译好的cL件中d日志代码?CODE>todir 属性指定将量cLC么地斏V?CODE>fileset 子元素指定测量哪?.class 文gQ?
<target name="instrument">
<cobertura-instrument todir="target/instrumented-classes">
<fileset dir="target/classes">
<include name="**/*.class"/>
</fileset>
</cobertura-instrument>
</target>
|
用通常q行试包的同一U类型的 Ant dq行试。惟一的区别在于:被测量的cdd原始cd现在c\径中之前出现在类路径中,而且需要将 Cobertura JAR 文gd到类路径中:
<target name="cover-test" depends="instrument">
<mkdir dir="${testreportdir}" />
<junit dir="./" failureproperty="test.failure" printSummary="yes"
fork="true" haltonerror="true">
<!-- Normally you can create this task by copying your existing JUnit
target, changing its name, and adding these next two lines.
You may need to change the locations to point to wherever
you've put the cobertura.jar file and the instrumented classes. -->
<classpath location="cobertura.jar"/>
<classpath location="target/instrumented-classes"/>
<classpath>
<fileset dir="${libdir}">
<include name="*.jar" />
</fileset>
<pathelement path="${testclassesdir}" />
<pathelement path="${classesdir}" />
</classpath>
<batchtest todir="${testreportdir}">
<fileset dir="src/java/test">
<include name="**/*Test.java" />
<include name="org/jaxen/javabean/*Test.java" />
</fileset>
</batchtest>
</junit>
</target>>
|
Jaxen 目使用 JUnit 作ؓ其测试框Ӟ但是 Cobertura 是不受框架媄响的。它?TestNG、Artima SuiteRunner、HTTPUni 或者在您自己在C室开发的pȝ中一样工作得很好?/P>
最后,cobertura-report
d生成本文开始部分看到的那个 HTML 文gQ?
<target name="coverage-report" depends="cover-test">
<cobertura-report srcdir="src/java/main" destdir="cobertura"/>
</target>
|
srcdir
属性指定原始的 .java 源代码在什么地斏V?CODE>destdir 属性指?Cobertura 攄输出 HTML 的那个目录的名称?/P>
在自q Ant ~译文g中加入了cM的Q务后Q就可以通过键入以下命o来生成一个覆盖报告:
% ant instrument
% ant cover-test
% ant coverage-report
|
当然Q如果您愿意的话Q还可以改变目标d的名Uͼ或者将q三Q务合qؓ一个目标Q务?
l束?/FONT>
Cobertura 是敏L序员工具׃新增的一个重要工兗通过生成代码覆盖率的具体数|Cobertura 单元测试从一U艺术{变ؓ一门科学。它可以L试覆盖中的I隙Q直接找?bug。测量代码覆盖率使您可以获得Lq修?bug 所需的信息,从而开发出Ҏ个h来说都更健壮的Y件?
参考资?
- 您可以参阅本文在 developerWorks 全球站点上的 英文原文?BR>
- ?SourceForge 下蝲 Cobertura?BR>
- 获得 JUnit 受媄响的试Q它是用?Java q_的实际标准单元测试框架?BR>
- 阅读 Dave Thomas ?Andy Hunt 合著?Pragmatic Unit Testing in Java With JUnit QPragmatic BookshelfQ?003Q?BR>
- Cenqua ?Clover 是一个更_致的、收费的试覆盖率工P它所做的工作实质上与 Cobertura 相同Q只是它做得更细致一些?BR>
- IBM Rational 提供了全面的试工具包来帮助您检代码。?A >Get started with automated testing: Road map to success”ؓ您提供了卛_开始测试自q代码所需的基知识?
- Mike Kelly 提供了?A >using TestManager to report test coverage”的l节QdeveloperWorksQ?003 q?12 月)?BR>
- PureCoverage ?IBM Rational PurifyPlus 试工具包的一部分Q它是一个出色的代码覆盖率工兗Rational Application Developor 包括 PureCoverage 功能Q从?A >使用 IBM Rational Application Developer for WebSphere Software q行lg试”中可以了解关于它的更多知识QdeveloperWorksQ?005 q?3 月)?BR>
- Cobertura ?jcoverage 的分支?
- Carnegie Mellon ?Software Engineering Institute 发表了对 McCabe's Cyclomatic Complexity 的详l说明?BR>
- David Carew ?Sandeep Desai 合著??A >Keeping critters out of your code: How to use WebSphere and JUnit to prevent programming bugs”(developerWorksQ?003 q?6 月) 分析了如何用 XP Ҏq行试?BR>
- Dennis M. Sosnoski 推出 ?Classworking 工具?/FONT> pdQ探讨开源的 Hansel ?Gretel 代码覆盖率工兗?BR>
- 参阅?A >Test your tests with Jester”,了解关于开放源代码 JUnit 试试器的知识?BR>
- Malcolm Davis 撰写的?A >利用 Ant ?JUnit q行增量开?/FONT>”(developerWorksQ?000 q?11 月)介绍了如何将 JUnit 集成到自q目中?BR>
- Eric Allen ?Roy Miller 在他们各自的专栏 诊断 Java 代码 ?Demystifying Extreme Programming 中经常讨论到单元试?BR>
- Erik Hatcher ?让编译和试q程自动?/FONT> QdeveloperWorksQ?001 q?8 月)展示了如何将增量试和连l编译结合到一个自动过E中?BR>
- 探烦 FoCuSQ这?IBM alphaWorks 的一个工P它实C功能覆盖ҎQƈ通过提供未测试区域的详细覆盖率信息来改进应用E序试?BR>
- 作ؓ本文分析对象?Jaxen 目 是一个用?Java ~程的开?XPath 引擎Q适合多种不同对象模型?
- ?developerWorks ?Java 技术专?/FONT> 中可以找到关?Java ~程的各个方面的文章?BR>
- 请参?Developer BookstoreQ以获得一些技术书c的完整清单Q其中包括数百本 Java 相关主题 的书c?BR>
 |
关于作?/FONT>
 |
|
Elliotte Rusty Harold 出生在新奥尔良,现在他还定期回老家喝一美味的U葵汤。不q目前,他和d Beth 定居在纽U͘q布鲁克林的 Prospect HeightsQ同住的q有他的猫咪 CharmQ取自夸克)?MarjorieQ取自他x的名字)。他?Polytechnic 大学计算机科学的副教授,讲授 Java 技术和面向对象~程。他?Cafe au Lait |站?Internet 上最受欢q的独立 Java 站点之一Q姊妹站?Cafe con Leche 已经成ؓ最受欢q的 XML 站点之一。他的著作包?Effective XML ?A > Processing XML with Java ?A > Java Network Programming ?The XML 1.1 Bible 。目前他正在从事 XML ?XOM API?A >Jaxen XPath 引擎?Jester 试覆盖率工L开发工作? |