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 SuiteTotal 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-1test_2== run test-2
4 @Parameters
加载环境变量
比如在初始化时,根据提供的参数区分开发环境与测试环境
@Parameters(value = {"jdbcUrl"})@BeforeSuitepublic void initSuit(String jdbcUrl) { String jdbcurl = jdbcUrl;}
那么parameter的值在哪设置呢? ——testng.xml
...
当然与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中指定工厂类
但是细想一下,测试方法的参数化可以通过@DataProvider实现
我理解的二者区别如下:
- @Factory实现测试类的动态创建,@DataProvider改变测试方法的实参
- @Factory实例化的类及属性对所有测试方法有效
- @Factory和@DataProvider可以一起使用,花式组合数据
6 @Ignore
应用于测试类,忽略该类下所有的测试方法
相当于@Test(enabled=false),不过是批量的