1 执行顺序
suit -> class -> method,包含before和after两种形式,刚好对应各阶段的初始化(setup)和清理(teardown)
另外test 和 groups可以定义不同的组合,比如指定某个功能模块(package),或者以提测版本号将测试类/方法分组
import org.testng.Assert;
import org.testng.annotations.*;
public class App {
private void log(String action) {
final String FORMATTER = "===== %-20s =====";
System.out.println(String.format(FORMATTER, action));
}
@BeforeSuite
public void beforeSuit() {
log("before suit");
}
@AfterSuite
public void afterSuit() {
log("after suit");
}
@BeforeClass
public void beforeClass() {
log("before class");
}
@AfterClass
public void afterClass() {
log("after class");
}
@BeforeMethod
public void beforeMethod() {
log("before method");
}
@AfterMethod
public void afterMethod() {
log("after method");
}
@Test
public void testAdd() {
log("test add");
Assert.assertEquals(4, 1 + 3);
}
}
---------------------------------------------------
===== before suit =====
===== before class =====
===== before method =====
===== test add =====
===== after method =====
===== after class =====
===== after suit =====
另外,除了@Test注解可以出现多次外,before/after + suit/test/class/method等也可以出现多次,不过貌似没多大意义
2 @Test参数
2-1 enabled
测试方法是否生效,默认为true;如果不希望执行,可以置为false
@Test(enabled = false)
2-2 dependsOnMethods/dependsOnGroups
测试方法的上游依赖,如果依赖的对象执行失败,则该测试方法不执行(skip)
public class App {
private void log(String action) {
final String FORMATTER = "===== %-20s =====";
System.out.println(String.format(FORMATTER, action));
}
@Test(dependsOnMethods = {"upMethod"})
public void downMethod() {
log("run down method");
Assert.assertEquals(2, 1+1);
}
@Test
public void upMethod() {
log("run up method");
Assert.assertEquals(3, 5/3);
}
}
------------------------------------------------
===============================================
Default Suite
Total tests run: 2, Failures: 1, Skips: 1
==============================================
2-3 expectedExceptions
Assert无法实现异常的校验,但无法避免异常的发生,或者希望人为地抛出异常,因此需要借助此参数
public class App {
@Test(expectedExceptions = {java.lang.ArithmeticException.class})
public void testExcption() {
int a = 5 / 0;
}
}
2-4 dataProvider
方法引用的测试数据,避免同一个方法书写多次,可以设计为数据驱动
public class App {
@DataProvider(name = "data")
public Object[][] data() {
return new Object[][] {
{1, 1},
{2, 4},
{3, 9},
};
}
@Test(dataProvider = "data")
public void testSquare(int num, int expected) {
int result = num * num;
Assert.assertEquals(result, expected);
}
}
3 @DataProvider
测试方法的数据提供者,必须返回Object[][]
对象
包含2个参数:
- name,唯一命名,且测试方法的dataProvider与其一致;如果为空,默认为方法名
- parallel,默认为false;在并行执行不影响测试执行时,设置为true可以提高任务的执行效率
3-1 由其他类提供数据
测试方法查找指定的dataProvider时,默认是当前类及其父类
如果数据由其他类提供,则需指定dataProviderClass,并且由@DataProvider注解的方法必须为static
public class App {
@Test(dataProvider = "data", dataProviderClass = DataDriver.class)
public void testSquare(int num, int expected) throws InterruptedException {
int result = num * num;
Assert.assertEquals(result, expected);
}
}
class DataDriver {
@DataProvider(name = "data", parallel = true)
public static Object[][] data() {
return new Object[][] {
{1, 1},
{2, 4},
{3, 9},
};
}
}
3-2 带参数的DataProvider
多个测试方法可以指定同一个DataProvider
DataProvider支持将Method作为第一个参数,根据不同的方法读取不同的数据文件,如Excel、Txt
public class App {
@Test(dataProvider = "dp", dataProviderClass = DataDriver.class)
public void test_1(String arg) {
System.out.println("== run test-1");
Assert.assertTrue(arg != null);
}
@Test(dataProvider = "dp", dataProviderClass = DataDriver.class)
public void test_2(String arg) {
System.out.println("== run test-2");
Assert.assertTrue(arg != null);
}
}
class DataDriver {
@DataProvider(name = "dp")
public static Object[][] data(Method method) {
System.out.println(method.getName());
return new Object[][]{
{"java"},
};
}
}
----------------------------------------------------
test_1
== run test-1
test_2
== run test-2
4 @Parameters
加载环境变量
比如在初始化时,根据提供的参数区分开发环境与测试环境
@Parameters(value = {"jdbcUrl"})
@BeforeSuite
public void initSuit(String jdbcUrl) {
String jdbcurl = jdbcUrl;
}
那么parameter的值在哪设置呢? ——testng.xml
<suite name="Mysuite">
<parameter name="jdbcUrl" value="jdbc:oracle:thin:@oraHost:1522:orcl"/>
<test name="Mytest">
...
当然与jenkins结合会更妙
Parameters are scoped. In testng.xml, you can declare them either under a <suite> tag or under <test>. If two parameters have the same name, it's the one defined in <test> that has precedence. This is convenient if you need to specify a parameter applicable to all your tests and override its value only for certain tests.*test标签下的参数优先级高于suit
5 @Factory
动态创建测试类
拿官网例子来讲:编写一个方法实现多次访问同一网页
// 官网使用了parameter,然后需要在testng.xml中配置多个参数值,实属麻烦
public class TestWebServer {
@Test(parameters = { "number-of-times" })
public void accessPage(int numberOfTimes) {
while (numberOfTimes-- > 0) {
// access the web page
}
}
}
那么使用@Factory将简化配置
public class WebTest {
private int m_numberOfTimes;
public WebTest(int numberOfTimes) {
m_numberOfTimes = numberOfTimes;
}
@Test
public void testServer() {
for (int i = 0; i < m_numberOfTimes; i++) {
// access the web page
}
}
}
-----------------------------------------------------
public class WebTestFactory {
@Factory
public Object[] createInstances() {
Object[] result = new Object[10];
for (int i = 0; i < 10; i++) {
result[i] = new WebTest(i * 10);
}
return result;
}
}
通过@Factory动态创建测试类,返回Object[]
对象 —— 包含一组测试类实例,框架会自动运行每个实例下的测试方法
不过,虽然简化了配置,但并不是指不用配置,需要在testng.xml中指定工厂类
<suite name="Suite1" verbose="1" >
<test name="Test1">
<classes>
<class name="WebTestFactory" />
<class name="WebTest" />
</classes>
</test>
</suite>
但是细想一下,测试方法的参数化可以通过@DataProvider实现
我理解的二者区别如下:
- @Factory实现测试类的动态创建,@DataProvider改变测试方法的实参
- @Factory实例化的类及属性对所有测试方法有效
- @Factory和@DataProvider可以一起使用,花式组合数据
6 @Ignore
应用于测试类,忽略该类下所有的测试方法
相当于@Test(enabled=false),不过是批量的