这篇文章将为大家介绍TestNG这个新的测试框架的特性,以及TestNG优于Junit3.X的地方
TestNG(Test Next Generation),顾名思义,下一代的测试框架。它是基于J2SE5.0的注释特性的而构建的轻量级的单元测试框架结构。说起单元测试框架,大家都会自然地联想到JUnit。用过JUnit3.X的程序开发人员,都会发现JUnit在提供了强大功能的同时,也存在很多令人沮丧的地方。其中一个问题就是,JUnit3.x
在每个测试方法调用前和调用后都会调用setUp()和tearDown()的方法。如果开发人员希望在不同的测试方法中重用同一个JDBC连接或者JNDI的Context的时候,会觉得很不方便。一般的解决这个问题的方法是使用静态方法,而这样的话,就必须小心并发控制的问题(多个线程访问共享的静态对象)。除此之外,JUnit
3.X对于多线程测试也比较麻烦,需要其他模块的支持。
这篇文章将为大家介绍TestNG这个新的测试框架的特性,以及TestNG优于Junit3.X的地方。众所周知,Eclipse不仅仅是功能强大的Java
IDE,同时也是一个开放的应用集成平台。而Eclipse3.1提供了对J2SE5.0的支持。因此,笔者将以Eclipse为运行环境,介绍Testng的安装,使用和运行。Eclipse3.1可以从http://www.eclipse.org/downloads/index.php下载。
由于TestNG是基于J2SE5.0的注释特性所构建的。因此读者在阅读本文之前,必须了解注释的一些基本概念。关于J2SE的注释特性,笔者曾经在另一篇文章中详细的介绍过,详细介绍请参考"参考资料"。这里只简单的介绍一些概念。
注释是J2SE5.0所新提供的对于元数据的支持。程序开发人员可以在不改变原有逻辑的情况下,在源文件嵌入一些补充的信息。注释都是由@Interface
annotationName 来声明的。注释可以用来修饰类定义,方法,域变量等等。使用的时候是在修饰的对象的定义前@annotationName。注释可以包含多个属性,使用的时候为属性赋值,例如
@annotationName(prop1=value1,prop2=value2)。程序的开发人员还可以通过Java的反射特性,在运行时获得这些注释的信息。在后面的章节中,大家会看到TestNG是如何使用它所定义的注释类型的来实现测试框架的。
在Eclipse中安装testNG很简单。和安装其他的plugin的方法相似。首先启动Eclipse3.1,在Help->Software
Update->Find and Install, 在弹出的向导中,选择"Search New Features
to Install", 点击"New Remote Site",如图1所示。在URL中输入 http://beust.com/eclipse,点击"OK"。如图2所示,点击"Finish",Eclipse会帮助你完成下面的安装。熟悉Eclipse的读者对这个过程一定不会觉得陌生。
图1 新建Update Site
图2 安装TestNG
安装好TestNG后,在Eclipse中单击"Window"->Show View->Other->Java->TestNG,
TestNG的视图就打开了。
图3 TestNG的视图
注意:TestNG的视图的作用时为了现实测试结果。为了显示视图的功能,图3的视图是运行了一个测试用例后的结果。读者如果是第一次打开视图,应该是空白的。
TestNG和JUnit不同,他使用注释、正则表达式和基于XML的配置文件对测试方法进行配置的。我们先来看一个简单的例子。
1) 在Eclipse中创建一个Java的项目,com.catherine.lab.testng.demo
2) 在Packet Explorer中,右键点击刚生成的项目,选择Properties。
3) 在Properties属性框中,选择"Java Build Path",点击"Add External
JARs…"
4) 在文件浏览的对话框中,选择{eclipse 3.1 home directory}/plugins/com.beust.testng.eclipse_XXX/eclipse_testng.jar,以及
{eclipse 3.1 home directory}/plugins/com.beust.testng.eclipse_XXX/lib/testng-jdk14.jar/以及testng-jdk15.jar.
点击OK
5) 在Project中创建一个package: com.catherine.lab.testng.firstTest。在package里边创建一个类:FristTestSample.
清单1 TestNG的第一个例子
package com.catherine.lab.testng.firstTest;
import com.beust.testng.annotations.*;
public class FirstTestSample {
public FirstTestSample() {
super();
}
@Test
public void testPass() {
assert true : "This test should pass.";
}
@Test
public void testFail() {
assert false : "This test will fail";
}
@Configuration(beforeTestClass = true)
public void doBeforeTests() {
System.out.println("invoke before test class!");
}
@Configuration(afterTestClass = true)
public void doAfterTests() {
System.out.println("invoke after test class!");
}
} |
6) 在Eclipse中打开Run->Run..,如图4所示。 首先在选择使用TestNG的Project,而后在选择编写了测试逻辑的Class,点击Run。测试结果就显示在TestNG的视图中了。如图5所示。
图4 配置运行TestNG的程序
图5 TestNG的运行结果
这是一个完整的测试用例。和JUnit不同,TestNG中实现测试逻辑的类不需要继承任何父类。测试方法也无需遵循testXXX的命名规则。
TestNG的类是大家所非常熟悉的普通的Java类,而在这个类中,所有的被@Test这个注释所修饰的方法都会被当作测试方法来运行。除了测试类之外,TestNG还需要了一个配置文件,用来配置测试过程。以下是一个简单的配置文件:testng.xml。
清单2 testNG的配置文件
<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" >
<suite name="My First TestNG test">
<test name="Hello Test!">
<classes>
<class name=" com.catherine.lab.testng.firstTest.FirstTestSample " />
</classes>
</test>
</suite> |
testng.xml可以配置测试套件<suite>,类似于JUnit的TestSuite。而<test>类似于JUnit中的TestCase。所不同的是,
TestNG中的测试套件可以包括多个测试用例,一个测试用例可以包括多个测试类,而一个测试类中可以定义多个测试方法。在下面的例子中,我们将看到这个配置文件更复杂的应用。
在图4的运行配置中,我们也可以设置一个xml文件作为配置文件,而不是直接使用测试类。其实我们使用测试类的时候,testNG也帮我们生成了一个缺省的xml文件。不相信的话,你可以切换到Resource
Perspective,然后刷新Workspace,就会发现这个project里边生成了一个xml文件,而这个文件就是TestNG的缺省的配置文件。
现在我们再回到清单1,大家在上面的程序清单中会发现,除了使用@Test这个注释以外,我们还使用了@Configuration这个注释。下面我们就来介绍@Configuration这个注释的用途。
在注释Configuration中,定义了以下的属性:
清单3 configuration中的属性
public boolean beforeSuite() default false;
public boolean afterSuite() default false;
public boolean beforeTest() default false;
public boolean afterTest() default false;
public boolean beforeTestClass() default false;
public boolean afterTestClass() default false;
public boolean beforeTestMethod() default false;
public boolean afterTestMethod() default false;
|
- beforeSuite=true,所修饰的方法将在测试套件(也就是配置文件中的Suite Tag)中任何一个方法调用之前,调用一次
- afterSuite=true,所修饰的方法将在测试套件中所有方法都调用过后,调用一次
- beforeTest=true,在测试用例(配置文件中Test Tag)中任何一个测试方法调用之前,调用一次
- afterTest=true, 在测试用例中任何所有方法都调用之后,调用一次
- beforeTestClass=true,在测试类中任何测试方法调用之前,调用一次
- afterTestClass=true,在这个测试类中所有方法都调用过后,调用一次
- beforeTestMethod=true,在每个测试方法调用之前,调用一次
- afterTestMethod=true,在每个测试方法调用之后,调用一次
这个清单1中doBeforeTests()方法,在任何一个test方法调用之前被调用一次。doAfterTests,就是所有的test方法运行过了以后再调用一次。从Console输出的信息中,我们可以验证这一点:
图6 console输出的运行信息
上一节中我们介绍了使用testNG的一个最简单的例子,这一节中我们将介绍一些关于testNG的高级应用。注释Test除了标志其修饰的方法为测试方法,
还提供了groups的属性。比如上面例子的两个方法testPass()和testFail(),我们可以给这两个方法加上group的属性。
清单4 测试@Test的groups属性
@Test(groups={"functional_test"})
public void testPass() {
assert true : "This test should pass.";
}
@Test(groups={"checkin_test"})
public void testFail() {
assert false : "This test will fail";
}
} |
而后打开Run->Run…,在配置文件的Runtime配置中选择Groups,然后选择你要运行的group的名字。
图7 运行选定的测试组
这个时候我们从TestNG中看到测试结果,只有testPass运行了,而testFail因为不属于funcational_test这个组,因此并没有运行。
图8 运行结果
和第一个例子类似,虽然我们在这里并没有显示地定义配置文件,testNG已经生成了相应的配置文件了。在Resource Perspective底下可以看到这个文件:Custom_SuiteXXXX.xml.
清单5 自动生成的配置文件
<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" >
<suite name="My First TestNG test">
<test name="Hello Test!">
<groups>
<run>
<include name="functional_test"/>
</run>
</groups>
<classes>
<class name=" com.catherine.lab.testng.firstTest.FirstTestSample " />
</classes>
</test>
</suite> |
除了groups属性以外,注释Test还支持属性dependsOnMethods和属性dependsOnGroups. 这两个属性主要用于规定测试方法的执行顺序。
TestNG并不保证按照定义的顺序执行测试方法。如果这些测试方法之间有依赖关系的话,那么我们就可以使用dependsOnXXXX的属性。我们还是看第一个例子,现在我们在这个例子里边增加了一个方法:
setupEnvforPass()。我们希望setupEnvforPass()方法在testPass方法前执行,我们修改了testPass的test注释。如清单6所示:
清单6 测试@Test的属性dependsOnMethods
@Test(groups={"functional_test"},
dependsOnMethods = { "setEnvForPass" })
public void testPass() {
assert true : "This test should pass.";
}
@Test(groups={"checkin_test"})
public void testFail() {
assert false : "This test will fail";
}
@Test(groups = {"init"})
public void setEnvForPass(){
assert true: "This is dependent method"
}
} |
运行配置和配置文件都不需要改动,现在我们来运行这个例子,测试结果如图9所示。大家可以看到,虽然我们在testPass方法之后定义了,setEnvForPass方法,但是由于我们将setEnvForPass定义为testPass的以来方法,setEnvForPass在testPass前执行了。
同样,我们可以定义dependsOnGroups的属性,这样只有Groups中所有的方法都被执行完,这个方法才会被执行。注意:如果depensOnGroups中制定的group在配置文件中被excluded了,那么这个方法会依然被执行。但是如果指定的group在配置文件中被include了,而group中的方法有错误的话,那么这个方法会被skip,不会被执行。
图9 运行结果
下面我们要介绍一个新的注释类型: @Parameter。
TestNG的测试方法可以带有参数,参数可以通过@Parameter来声明,具体的参数值在testng.xml中定义。这是testng的一个很优越的特性。我们还是在以前的例子上的基础上来验证这个特性。我们为setEnvForPass这个方法定义一个参数,target_server,并且在测试方法中打印这个参数。
清单7 测试注释Parameter
@Parameters({ "target_server" })
@Test(groups = {"init"})
public void setEnvForPass(String targetServer){
assert true: "This is dependent method";
System.out.println(targetServer);
}
} |
Target_server的值在testng.xml中定义。在TestNG的运行时配置中选择Suite,然后Browse清单8中定义好的的testng.xml。运行TestNG,我们从Console的运行结果中看到,target_server的值被打印出来了。
清单8 自定义的配置文件
<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd">
<suite name="Custom suite">
<parameter name="target_server" value="127.0.0.1"/>
<test verbose="6" name="Test for 1 classes" annotations="1.5">
<groups>
<run>
<include name="functional_test"/>
</run>
</groups>
<classes>
<class name="com.catherine.lab.testng.firstTest.FirstTestSample">
</class>
</classes>
</test>
</suite> |
测试方法的参数可以是任意多个,只要你通过配置文件传入了正确的参数,那么测试方法中就可以使用这些参数了。不过需要注意的是,参数是有作用域的,比如参数可以在配置文件的suite和test之后定义,而如果两个参数的名称一样,test中定义的参数值有较高的优先级。
testNG可以从多个线程中运行测试方法,只需要将配置文件中suite的parallel属性设为true。线程的数目在thread-count中设置。如果两个方法有依赖关系,那么他们将在一个线程中运行,除此之外,都可以在多个线程中并发的运行。
<suite name="My suite" parallel="true" thread-count="10">
除了以上介绍的特性以外,(请参阅"参考资料")
- TestNG提供了注释Factory,用来动态生成测试方法的参数。
- TestNG还提供了AntTask <testng>,可以从Ant脚本中调用testNG。
- TestNG的可以调用JUnit的test cases。只需要配置文件中声明
<test name="Test1" junit="true">
<classes ..>
- TestNG还可以从程序中调用:
TestNG testng = new TestNG();
testng.setTestClasses(Class[] {});
testng.run();
除了TestNG之外… 从上面的例子可以看出,TestNG这个单元测试框架的功能是很强大的,而且简单易学。开发者只需要使用TestNG所提供的注释和正确的配置文件,可以轻松地完成复杂的测试用例。
除了TestNG之外,JTiger也是一种基于J2SE5.0的单元测试框架,其中应用了大量J2SE5.0的新特性,比如注释和静态Import。和TestNG类似,
JTiger提供了大量内建的注释类型, 比如JTiger也使用注释@Test标明测试方法, 使用注释@Category表示这个测试方法属于那一类,类似于TestNG的@Test的groups属性。和TestNG不同的是,JTiger并没有使外部的配置文件。
总之,TestNG和JTiger都解决了JUnit3.x中存在的问题,提供了大量优于JUnit3.x的特性。而JUnit也并没有就此止步,即将发布的JUnit4.0有了根本性的变化,JUnit4.0也将变成基于注释的测试系统。同样也将提供大量的内建注释类型:比如@Test,
@Before, @After等等。这些引入的注释类型,使得JUnit克服了以前的问题,拥有了新的活力。Justine Lee在他的文章中,详细地比较了这三种测试框架,请参阅"参考资料"。
选用何种测试框架,取决于很多的因素。虽然JUnit具有众多的拥簇者,但是TestNG和JTiger的崛起也不可小觑的。应该说,TestNG是建立在JUnit3.x之上的,吸取了JUnit的优点,同时也摈弃和改正了JUnit的缺点。笔者曾经在Eclipse中使用过JUnit3.x和TestNG,个人认为TestNG使用起来比JUnit3.x要更为方便。但是JUnit提供测试插件(plug-in)的功能,TestNG目前并没有提供这种功能。不过我们有理由相信,在不久的将来TestNG会对于eclipse插件提供更为丰富的支持。本文通过对TestNG的介绍,希望能够为大家在选择测试框架的时候提供一个新的选择。
- Filippo Diotalevi 在2005年1月在developerWorks上简单介绍了testNG,
- 如果不熟悉 Java 注释,请阅读 Brett McLaughlin 2004 年 9 月份开始在 developerWorks
上发表的由两部分组成的系列文章:
- 请阅读Justin Lee 2005年7月的文章:测试框架的比较
- 如果需要参考TestNG的使用手册,请阅读:TestNG
用户手册
|