原文地址:http://www.ibm.com/developerworks/cn/java/j-cq08296/
在每个现代软件包的构造阶段,测试这一实践都扮演着中心角色。过去那种先编写代码,然后有空的时候再测试(或者根本不测试)的日子已经一去不返,因为大多数开发人员现在认识到需要采用编码和测试彼此交织、同步推进的软件方法论,以便尽早发现 bug,在开发过程开始的时候就识别出主要的风险。
JUnit 超过了其他测试框架,推动开发人员理解了测试尤其是单元测试的用途。利用一个相当简单、实用、严格的架构,JUnit 已经能够“传染”大量开发人员。(有关“被测试传染”的更多信息,请参阅 参考资料。) JUnit 用户已经学会了单元测试的一些基本规则:
但是,随着开发人员对测试的信任增长,JUnit 的简单性和严格性把他们分成两个相反的派别。一方面,有些人坚信 JUnit 的简单性对于不断地提醒程序员软件也必须保持简单来说是必不可少的(这称为 KISS 原则,代表 keep it simple, stupid);另一方面,有些人认为 JUnit 不是简单而是简化,所以他们想要从测试框架得到新的高级特性、更大的灵活性和更强大的能力。JUnit 的一些特殊特性,就是为了满足这个群体的一些具体批评而推出的:
TestCase
类的限制很大。setUp()
和 tearDown()
方法传递参数。为了演示 TestNG 的用法,我要为叫做 Jakarta Common Lang 的这个广泛应用的开源库(其中包含一些处理和操纵字符串、数字和 Java 对象的有用的类)编写一些单元测试。在下面的 参考资料一节中,您可以找到 TestNG 和 Jakarta Common Lang 库的链接;如果您想在自己的机器上随着本文一起练习,这二者都需要下载。
可以在两个不同的包中得到 TestNG:一个包要求 JDK 5.0,另一个包与 Java 语言 1.4 版本兼容。定义测试的时候,它们使用的语法略有差异:前者使用 JDK 5.0 标注,后者使用旧的 Javadoc 风格的标注。本文使用的是 JDK 5.0 版本,所以在继续阅读本文之前,需要对标注有基本的了解;您可以在 参考资料中找到关于这个主题的 developerWorks 资源的链接。但是,您要知道 只有在编译和运行测试的时候才需要 JDK 5.0,所以您仍然可以用自己喜欢的编译器来构建应用程序。实际上,您将用从 Jakarata 项目的 Web 站点下载的相同 JAR 文件来测试 Jakarta Common Lang 库。关于使用 Java 平台 1.4 版本的 TestNG 的更多细节,可以在 TestNG 的 Web 站点上找到。
@Test
通知框架这个类的方法是测试。清单 1 演示了实用类
StringUtils
的一个最简单的测试。它测试
StringUtils
的两个方法:
isEmpty()
方法检测
String
是否为空;
trim()
方法从
String
两端删除控制字符。请注意,其中使用了 Java 指令
assert
来检测错误情况。
package tests; import com.beust.testng.annotations.*; import org.apache.commons.lang.StringUtils; public class StringUtilsTest { @Test public void isEmpty() { assert StringUtils.isBlank(null); assert StringUtils.isBlank(""); } @Test public void trim() { assert "foo".equals(StringUtils.trim(" foo ")); } }
StringUtilsTest
类完成。
<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" > <suite name="My test suite"> <test name="First test"> <classes> <class name="tests.StringUtilsTest" /> </classes> </test> </suite>
如果这个示例 testng.xml 文件看起来没什么用处(只有一个测试类),那么好消息是:这实际上是您定义测试套件时 惟一需要编写的文件。还记得 JUnit 过去的日子么?在那些日子里,套件的定义可能分布在多个文件中:JUnit 的 TestSuite
文件,属性文件,还有当然缺不了的 Ant 构建文件。使用 TestNG,所有必需的数据都集中在 testng.xml 文件中。不需要额外的 TestSuite
文件和构建文件。
要运行测试,请用 javac
编译类,然后用以下命令调用 TestNG :
java -ea -classpath .;testng.jar;commons-lang-2.0.jar com.beust.testng.TestNG testng.xml
-ea
告诉 JVM 处理断言(在断言失败时抛出异常);运行这个例子只需要 testng.jar 和 commons-lang-2.0.jar 这两个库,而
com.beust.testng.TestNG
是 TestNG 的主类。对于所有那些已经非常高兴地忘记了
java
和
javac
的神秘语法的开发人员来说,还提供了一个有用的 Ant 任务。作为例子,清单 3 演示了本文发布的示例应用程序的 Ant 构建文件。请注意与类
com.beust.testng.TestNGAntTask
关联的
testng
任务的定义,以及它在
test
目标中相当简单的用法。
<project name="sample" default="test" basedir="."> <!-- COMPILE TESTS--> <path id="cpath"> <pathelement location="testng.jar"/> <pathelement location="commons-lang-2.0.jar"/> </path> <target name="compile"> <echo message="compiling tests"/> <mkdir dir="classes"/> <javac debug="true" source="1.5" classpathref="cpath" srcdir="src" destdir="classes"/> </target> <!-- RUN TESTS--> <taskdef name="testng" classname="com.beust.testng.TestNGAntTask" classpathref="cpath"/> <path id="runpath"> <path refid="cpath"/> <pathelement location="classes"/> </path> <target name="test" depends="compile"> <echo message="running tests"/> <testng fork="yes" classpathref="runpath" outputDir="test-output"> <fileset dir="src" includes="testng.xml"/> <jvmarg value="-ea" /> </testng> </target> </project>
@Test
标注的参数,使用的语法如下:@Test(groups = {"tests.string"})在这个具体的例子中,您声明:标注的方法属于
tests.string
组。因为参数
groups
是一个数组,所以可以指定多个组,组名之间用逗号分隔。例如,在示例应用程序中,您可以为
String
、Number 以及 boolean 创建不同的测试,然后如清单 4 所示配置 TestNG, 有选择地运行它们 .
<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" > <suite name="My suite"> <test name="Simple example"> <groups> <run> <include name="tests.string" /> <include name="tests.math" /> <exclude name="tests.boolean"/> </run> </groups> <classes> .... list classes here.... </classes> </test> </suite>显然,当运行不同的测试组时,HTML 报告能够在单一列表中显示所有测试,也可以在独立的列表中显示每个组的测试,从而能够立即理解问题的来源。
@ExpectedExceptions
标注可以使代码编写惊人地容易和简单。 @ExpectedExceptions
标注指明框架能够容忍抛出的NumberFormatException
异常,所以不应当被当作是故障。要查看在某行代码中是否抛出异常,您可以直接在这行代码之后加入 assert false
语句。这意味着 只有在指定行中抛出特定类型的异常的时候,您才会通过测试。