2.3 TestNG 注解
TestNG 和其他很多 Java 框架(如 JUnit、Spring 等)一样,使用了大量的注解。被不同注解修饰的类、方法具有不同的含义,本节对 TestNG 注解进行介绍,并按照使用场景把注解分成 4 类。
(1)前置条件和后置条件
把注解作为前置条件(或初始化操作)和后置条件(或清理操作)使用。
(2)数据驱动
TestNG 的特点之一是数据驱动,即测试用例和测试数据分离,以便维护和管理。
(3)测试用例
该分类只有一个@Test 注解。@Test 注解的作用是对测试用例进行控制,该注解中的方法有很多,后面会对常用方法进行介绍。
(4)监听器
该分类只有一个@Listeners 注解。监听器的作用是监控测试过程,如果采用默认监听器,则不需要任何配置;如果使用自定义监听器,则需要使用@Listeners 注解或 testng.xml 文件进行配置。由于篇幅所限,本章不对自定义监听器进行介绍,有兴趣的读者可以自行查阅相关资料。
2.3.1 前置条件和后置条件
先来看看各注解的含义。
@BeforeSuite:在该 Suite 的所有 Test 都未运行之前运行。
@AfterSuite:在该 Suite 的所有 Test 都运行之后运行。
一个 Suite 对应一个顶级模块,比如一个软件项目分为 4 个模块,那么每个模块就是一个 Suite。一般结合 testng.xml 文件中的<;suite>;或<;suite-files>;标签进行使用。
@BeforeTest:在该 Test 的所有 Class 都未运行之前运行。
@AfterTest:在该 Test 的所有 Class 都运行之后运行。
一个 Test 对应一个子模块,一般结合 testng.xml 文件中的<;test>;标签进行使用。
@BeforeClass:在该 Class 的所有@Test 方法都未运行之前运行。
@AfterClass:在该 Class 的所有@Test 方法都运行之后运行。
一个 Class 对应一个 Java 类,在该 Java 类中,用@BeforeClass(或@AfterClass)修饰的方法会在该 Class 的所有@Test 方法都运行之前(或之后)运行。
@BeforeMethod:在该 Class 的每个@Test 方法运行之前运行。
@AfterMethod:在该 Class 的每个@Test 方法运行之后运行。
一个 Method 对应一个 Java 方法,在 Java 类中用@BeforeMethod(或@AfterMethod)修饰的方法会在该 Class 的每个@Test 方法 运行之前(或之后)运行。
@BeforeGroups:在该 Class 第一个分组的@Test 方法运行之前运行。
@AfterGroups:在该 Class 最后一个分组的@Test 方法运行之后运行。
Group 的控制粒度介于 Class 和 Method 之间,一个 Class 可以包含多个 Group,一个 Group 可以包含多个 Method。
只看文字是很生硬的,下面通过例子来说明以上注解。删除 FirstClassTest 中的内容,输入以下代码:
,在「FirstClassTest.java」上用鼠标右击,从弹出的快捷菜单中选择「Run As → TestNG Test」选项,此时 Eclipse 的控制台输出如下:
下面对运行结果进行说明。
① 可以看出@BeforeSuite、@AfterSuite、@BeforeTest、@AfterTest、@BeforeClass 和@AfterClass 控制测试执行的粒度是不同的,即 Suite > Test > Class
② 一个测试用例(@Test 修饰的 Java 方法)可以属于多个分组,比如上面示例的 testCase4。当多个分组都设置了对应的@BeforeGroups 和@AfterGroups 注解时,执行顺序是 Before Group1→Before Group2→After Group2→After Group1
③ @BeforeMethod 和@AfterMethod 共执行了 4 次,因为有 4 个测试用例
GroupsOnClass1
@Test(groups = "stu")
public class GroupsOnClass1 {
public void stu1(){
System.out.println("GroupsOnClass1中的stu1111运行");
}
public void stu2(){
System.out.println("GroupsOnClass1中的stu2222运行");
}
}
GroupsOnClass2
@Test(groups = "stu")
public class GroupsOnClass2 {
public void stu1(){
System.out.println("GroupsOnClass2中的stu1111运行");
}
public void stu2(){
System.out.println("GroupsOnClass2中的stu2222运行");
}
}
GroupsOnClass3
@Test(groups = "teacher")
public class GroupsOnClass3 {
public void teacher1(){
System.out.println("GroupsOnClass3中的teacher1运行");
}
public void teacher2(){
System.out.println("GroupsOnClass3中的teacher2运行");
}
}
groupsOnClass.xml
<?xml version="1.0" encoding="UTF-8" ?>
<suite name="suitename">
<test name="runAll">
<classes>
<class name="com.course.testng.groups.GroupsOnClass1"/>
<class name="com.course.testng.groups.GroupsOnClass2"/>
<class name="com.course.testng.groups.GroupsOnClass3"/>
</classes>
</test>
<test name="onlyRunStu">
<groups>
<run>
<include name="stu"/>
</run>
</groups>
<classes>
<class name="com.course.testng.groups.GroupsOnClass1"/>
<class name="com.course.testng.groups.GroupsOnClass2"/>
<class name="com.course.testng.groups.GroupsOnClass3"/>
</classes>
</test>
</suite>
View Code
GroupsOnMethod
public class GroupsOnMethod {
@Test(groups = "server")
public void test1(){
System.out.println("这是服务端组的测试方法11111");
}
@Test(groups = "server")
public void test2(){
System.out.println("这是服务端组的测试方法2222");
}
@Test(groups = "client")
public void test3(){
System.out.println("这是客户端组的测试方法33333");
}
@Test(groups = "client")
public void test4(){
System.out.println("这是客户端组的测试方法4444");
}
@BeforeGroups("server")
public void beforeGroupsOnServer(){
System.out.println("这是服务端组运行之前运行的方法");
}
@AfterGroups("server")
public void afterGroupsOnServer(){
System.out.println("这是服务端组运行之后运行的方法!!!!!");
}
@BeforeGroups("client")
public void beforeGroupsOnClient(){
System.out.println("这是客户端组运行之前运行的方法");
}
@AfterGroups("client")
public void afterGroupsOnClient(){
System.out.println("这是客户端组运行之后运行的方法!!!!!");
}
}
View Code
DependTest
public class DependTest {
@Test
public void test1(){
System.out.println("test1 run");
throw new RuntimeException();
}
@Test(dependsOnMethods = {"test1"})
public void test2(){
System.out.println("test2 run");
}
}
2.3.2 数据驱动
TestNG 做数据驱动时使用了@DataProvider 和@Parameters 注解,后者需要和 testng.xml 文件配合。举一个登录的场景,每种不同的输入都对应了不同的提示。
删除 FirstClassTest 中的内容,输入以下代码:
package cn.edu.bjut.testng;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class FirstClassTest3 {
@Test(dataProvider = "data")
public void testCase1(String username ,String password ,String prompt) {
System.out.println("如果输入:" + username +"、" + password +",提示" + prompt);
}
@DataProvider(name = "data")
public Object[][] dataProvider1(){
return new Object[][] {
new Object[] {"空账号","正确密码","账号不能为空!"},
new Object[] {"正确账号","空密码","密码不能为空!"},
new Object[] {"正确账号","正确密码","登陆成功!"}
};
}
}
保存代码,在 FirstClassTest.java 上用鼠标右击,从弹出的快捷菜单中选择「Run As → TestNG Test」选项,此时 Eclipse 的控制台输出如下:
,该方法返回一个对象二维数组。如果一个测试用例需要该数据,那么就通过@Test 注解的 dataProvider 属性传入数据提供者的名称name。
2.3.3 测试用例
@Test 注解的方法很多,前面已经介绍过 groups 和 dataProvider 了,下面再介绍几种常用的。
删除 FirstClassTest 中的内容,输入以下代码:
package cn.edu.bjut.testng;
import org.testng.annotations.Test;
public class FirstClassTest4 {
@Test(description = "测试用例1")
public void testCase1() {
System.out.println("testCase1");
}
@Test(priority = 2)
public void testCase2() {
System.out.println("testCase2");
}
@Test(priority = 1)
public void testCase3() {
System.out.println("testCase3");
}
@Test()
public void testCase4() {
System.out.println("testCase4");
throw new RuntimeException("testCase4运行异常!");
}
@Test(groups = { "myGroup" })
public void testCase5() {
System.out.println("testCase5");
throw new RuntimeException("testCase5运行异常!");
}
@Test(enabled = false)
public void testCase6() {
System.out.println("testCase6");
}
@Test(dependsOnMethods = {"testCase4"},dependsOnGroups = {"myGroup"},alwaysRun = true)
public void testCase7() {
System.out.println("testCase7");
}
}
保存代码,在「FirstClassTest.java」上用鼠标右击,从弹出的快捷菜单中选择「Run As → TestNG Test」选项,此时 Eclipse 的控制台输出如下:
下面对运行结果进行说明。
①description 代表测试用例描述,控制台会打印输出该描述。
②priority 代表优先级,数字越小,优先级越高,默认值为 0。testCase2 的 priority 值为 2,会最后一个执行;testCase3 的 priority 值为 1,会倒数第二个执行。如果级别一样,则执行顺序默认按方法名排序。
③enabled 的默认值为 true,代表启用。当 enabled 的值为 false 时,表示禁用,因此 testCase6 并未执行。
④testCase4 和 testCase5 都抛出了运行时异常,因此执行失败。
⑤dependsOnMethods 代表依赖一个或多个方法,dependsOnGroups 代表依赖一个或多个分组。一旦被依赖的测试用例执行失败,则 TestNG 将跳过该测试用例。但没有跳过 testCase7,原因是 testCase7 加了 alwaysRun 方法,并将值设为 true,代表始终执行,在默认情况下,该值为 false。建议尽量不要使用 dependsOnMethods 和 dependsOnGroups,因为违背了测试用例需要解耦的原则。