1. JDK 5 Annotations (JDK 1.4 可以用JavaDoc代替).
一个典型的Junit的测试类
import junit.framework.TestCase;
public class Jtest extends TestCase {
protected void setUp() throws Exception {
super.setUp();
//some initial code
}
protected void tearDown() throws Exception {
super.tearDown ();
//release resource and rollback
}
public void testFunction {
//test code
}
}
对应的TestNG的测试类,我们写最简单的情况。
public class NGtest{
@Configuration(beforeTestClass=true)
public void setUp() {
//some initial code
}
@Configuration(afterTestClass = true)
public void tearDown (){
//release resource and rollback
}
@Test( )
public void testFunction (){
//test code
}
}
这样写可以比较明显的看到两者的对应关系(注意TestNG的方法的名字是可以随便取的,取一样的名字只是为了让你容易找到对应的关系)
这说明TestNG是从Junit发展而来的,至少借鉴了很多Junit的思想(实际上TestNG的作者本身就是Junit的小组成员之一)。
我们从最表面的现象来看看吧
l TestNG没有继承任何类,甚至接口!!
l JDK 5 Annotations
这2者是息息相关的,为什么我们不用继承任何类,因为信息都在注释里面,这样会带来很多好处(绝不仅仅是命名的方便)。
2. 灵活的test configuration
先看一下Junit的执行顺序
Setup( ) test1( ) tearDown( ) Setup( ) test2( ) tearDown( )…………
下面是TestNG的
3. TestNG 的灵魂
配置文件(testng.xml)
Junit中要定义测试任务是要写TestSuit的,居然要写硬编码,而TestNG全部写在testng.xml(名字可以自定义的)中的,然后可以通过ant来调用。
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="my suite">
<test name="test1">
<groups>
<run name="group1">
</run>
</groups>
<classes>
<class name="ClassA" />
</classes>
</test>
<test name="test2" >
<groups>
<run name="group2">
</run>
</groups>
<classes>
<class name="ClassB"/>
<class name="ClassC"/>
</classes>
</test>
</suite>
上面包含了配置文件的基本的主干,一个配置文件只有一个Suite,基本上一个项目写一个配置文件就可以了,当然如果你的项目足够大,可能需要几个配置文件。测试是按照从大到小的顺序进行的,先执行suite,test,class,其中的group和class是平级的,在讲group的时候再详细的解释。有了这个文件,我们就可以很清楚的理解@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;
从字面意思可以看出方法的执行顺序,唯一的疑惑是执行的次数,官方的文档的解释是相当让人疑惑的,好在我们可以自己测试,beforeTestMethod是当类中任何方法调用都要执行的,beforeTest和beforeTestClass在一个Test中是只执行一次的(没试过把2个相同的类写到一个Test里面),而beforeSuite在一个配置文件中只执行一次。上面都没有考虑分组的情况,分组会更加的复杂,似乎灵活过头了,但是考虑到项目的复杂性,每个测试方法的初始化都可能不同,现在我们做的项目要求每天要将单元测试写进daily build的build文件里面自动执行,似乎只有用TestNG这样灵活的配置才能达到。
4. 分组
将一个测试方法或者配置方法分组是很容易的。
@Configuration(beforeTestClass=true,groups=“group1”)
@Test(groups=”group1”)
只要象上面写就可以了,名字随便取,而且不需要预先定义。
我们在一个类做2个组,看看效果
@Configuration(beforeTestClass=true,groups=“group1”)
Public void C1(){}
@Test(groups=”group1”)
Public void T1(){}
@Configuration(beforeTestClass=true,groups=“group2”)
Public void C2(){}
@Test(groups=”group2”)
Public void T2(){}
只选group1
…………………
<groups>
<run name="group1">
</run>
</groups>
…………
执行C1 T1
只选group2
…………………
<groups>
<run name="group2">
</run>
</groups>
…………
执行C2 T2
2个都选
…………………
<groups>
<run name="group1">
<run name="group2">
</run>
</groups>
…………
我最先以为的顺序是C1 T1 C2 T2 (A)
然而实际上是 C1 C2 T1 C1 C2 T2 (B)
要想达到(A)的效果,只能把2个group分开了放在不同的Test里面,其实只要知道一点,TestNG是先找Class,然后才去找Group的。
4. 参数
Junit是不带任何参数的,不论是测试方法还是配置方法,而TestNG都是可以添加参数的,有2种方法。
(1) 使用Parameter参数
@Parameters({ "first-name" })
@Test
public void testSingleString(String firstName) {
System.out.println("Invoked testString " + firstName);
assert "Cedric".equals(firstName);
}
参数的值放到配置文件中
<suite name="My suite">
<parameter name="first-name" value="Cedric"/>
<test name="Simple example">
这种方法偶尔用之还可以,但是很遗憾的是第一只能传String(可能可以其他的基本数据类型??但至少不能传复杂对象),第二数据写在配置文件中,不能所见即所得。
但是下面的方法真的给我们惊喜。
(2) DataProvider
这是从4.0以后增加的功能,看看怎么实现。
// This method will provide data to any test method that declares that its Data Provider
// is named "test1"
@DataProvider(name = "test1")
public Object[][] createData1() {
return new Object[][] {
new Object[] { "Cedric", new Integer(36) },
new Object[] { "Anne", new Integer(37)},
};
}
// This test method declares that its data should be supplied by the Data Provider
// named "test1"
@Test(dataProvider = "test1")
public void verifyData1(String n1, Integer n2) {
System.out.println(n1 + " " + n2);
}
执行的结果
Cedric 36
Anne 37
DataProvider返回的是个2维数组的对象,什么数据都可以提供了吧,而且可以让你的测试方法执行多次。而且最重要的是和测试方法写在一起,所见即所得。
5. Ant的集成和Daily Build
TestNG对Ant提供了很好的支持,这是我写得测试用的Ant脚本
<project default="testng">
<property file="build.properties" />
<path id="cpath">
<fileset dir="lib">
<include name="*.jar"/>
</fileset>
</path>
//定义testng的任务
<taskdef resource="testngtasks" classpath="lib/testng-4.4-jdk15.jar" />
<target name="compile">
<mkdir dir="test/classes"/>
<javac destdir="test/classes"
srcdir="${testng.dir}"
debug="true"
encoding="GBK" >
<classpath refid="cpath"/>
</javac>
</target>
//执行testng的任务
<target name="testng" depends="compile">
<testng classpath="test/classes">
<xmlfileset dir="${configure.dir}" includes="testing.xml" />
</testng>
</target>
</project>
简单的不能在简单了,你只要告诉ant来调用,然后告诉ant配置文件在哪里,剩下的事都交给testng自己的配置文件去做就行了,然后ant以后都不用修改了。不建议将任务的细节写在ant里面,首先功能不如testng自己的配置文件强大,而且ant需要维护。
下面基本上是TestNG才有的特点了,和Junit没有什么关系,但为了延续,还是用了上面的题目。
7 .依赖关系
先给个例子
@Test
public void serverStartedOk() {}
@Test(dependsOnMethods = { "serverStartedOk" })
public void method1() {}
在这个例子中,method1()必须在serverStartedOk()执行后才能执行,而且serverStartedOk()不能fail,否则method1()会被skip掉
基本上类似于ant的依赖关系,也很容易理解,只是分为强依靠和弱依靠,区别是弱依靠只管执行的顺序,强依靠除了顺序,还要正确,否则后面的不执行,上面的例子是强依靠,下面是弱依靠,加上alwaysrun=”true”
@Test
public void serverStartedOk() {}
@Test(dependsOnMethods = { "serverStartedOk" },alwaysrun=”true”)
public void method1() {}
如果有兴趣,可以看看下面的文章,里面有有趣的争论
http://beust.com/weblog/archives/000171.html
8.其他特性
还有很多,工厂模式,并行运行(Parallel running ),BeanShell 等等,感觉不是特别重要,而且写了那么多感觉好累啊,就不写了,有兴趣的可以去看看官方的文档。
http://testng.org/doc/documentation-main.html