简介: TestNG的官方文档的中文翻译版第五节,原文请见 http://testng.org/doc/documentation-main.htm 5 - Test methods, Test classes and Test groups。
目录
5 - Test methods, Test classes and Test groups
5.6.1 - Parameters from testng.xml
5.6.2 - Paramters with DataProviders
5.6.3 - Parameters from System Properties
5.7.1 - Dependencies with annotations
5 - Test methods, Test classes and Test groups
5.1 - Test groups
用 @Test 注解的方法。被 @Test 注解的方法的返回值会被忽略,除非你在 testng.xml 文件中将 allow-return-values 设置为 true 。
<suite allow-return-values="true">
or
<test allow-return-values="true">
5.2 - Test Groups
TestNG允许您执行复杂的测试方法分组。你不仅可以声明方法属于组,而且可以指定分组包含其他分组。然后TestNG可以被调用,并被要求包含某些分组和排除其他的分组。这为您在如何划分测试方面提供了最大的灵活性,并且如果您想要连续运行两组不同的测试,则不需要重新编译任何内容。
在 你的testng.xml 文件中指定测试组,可以在 <test> 或 <suite> 标签下找到。在<suite> 标签中指定的组,应用于其 <suite>标签下的所有的 <test> 标签。注意,组在这些标签中是累积的:如果在 <suite> 中指定组“a”,在 <test> 中指定组“b”,那么“a”和“b”都将被包含。
例如,有至少两类测试是很常见的:
* Check-in tests. 这些测试将在提交新代码之前运行. 它们通常被要求快速而且仅仅确保没有基础功能被破坏。
* Functional tests. 这些测试应该涵盖软件的所有功能,并且每天至少运行一次,尽管理想情况下您希望连续运行它们。
通常,Check-in 测试是功能测试的一个子集。TestNG允许您以一种非常直观的方式指定测试组。例如,你可以这样构造你的测试:你的整个测试类属于“functest”组,另外一些方法属于“checkintest”组:
public class Test1 {
@Test(groups = { "functest", "checkintest" })
public void testMethod1() {
}
@Test(groups = {"functest", "checkintest"} )
public void testMethod2() {
}
@Test(groups = { "functest" })
public void testMethod3() {
}
}
用以下调用TestNG
<test name="Test1">
<groups>
<run>
<include name="functest"/>
</run>
</groups>
<classes>
<class name="example1.Test1"/>
</classes>
</test>
将运行该类中的所有测试方法,而使用 checktest 调用它将只运行testMethod1()和testMethod2()。
下面是另一个例子,这次使用正则表达式。假设某些测试方法可能无法在Linux上运行,测试将是类似如此:
@Test
public class Test1 {
@Test(groups = { "windows.checkintest" })
public void testWindowsOnly() {
}
@Test(groups = {"linux.checkintest"} )
public void testLinuxOnly() {
}
@Test(groups = { "windows.functest" )
public void testWindowsToo() {
}
}
你可以使用下面的testng.xml文件只启动Windows方法:
<test name="Test1">
<groups>
<run>
<include name="windows.*"/>
</run>
</groups>
<classes>
<class name="example1.Test1"/>
</classes>
</test>
注意:TestNG使用正则表达,而不是wildmats。注意这个差别 (例如, "任何字符" 使用 ".*"匹配,-- .* -- 而不是 "*")
Method groups
同样可以包含或排除个别方法:
<test name="Test1">
<classes>
<class name="example1.Test1">
<methods>
<include name=".*enabledTestMethod.*"/>
<exclude name=".*brokenTestMethod.*"/>
</methods>
</class>
</classes>
</test>
在不需要重新编译任何东西的情况下,禁用单个方法非常方便,但我不建议过多地使用这种技术,因为如果开始重构Java代码,它可能会破坏测试框架(在标记中使用的正则表达式可能不再与方法匹配)。
5.3 Groups of groups
组还可以包括其他组。这些组被称为“MetaGroups”。例如,您可能想要定义一个组“all”,这个组包括“checkintest”组和“functest”组。“functest”组本身又将包含组“windows”和“linux”,而“checkintest”组又将只包含“windows”。下面是你将如何在你的配置文件中定义它
<test name="Regression1">
<groups>
<define name="functest">
<include name="windows"/>
<include name="linux"/>
</define>
<define name="checkintest">
<include name="windows"/>
</define>
<define name="all">
<include name="functest"/>
<include name="checkintest"/>
</define>
<run>
<include name="all"/>
</run>
</groups>
<classes>
<class name="test.sample.Test1"/>
</classes>
</test>
5.4 - Exclusion groups
TestNG 允许包含组也允许排除组.
例如,当由因为最近的修改而临时破坏的测试,但又没有时间去修复它们时非常有用。无论如何,你想要干净的运行功能性测试,因此你想要停用这些测试(被中断的测试),但是记住它们需要重新被激活。
解决这个问题的一个简单方法是创建一个称为"broken"的组并让这些测试方法归属它。例如,在上面的例子中,我知道testMethod2() 现在被破坏了,所有我想关闭它:
@Test(groups = {"checkintest", "broken"} )
public void testMethod2() {
}
现在我所想要做的只是在运行中排除这个组:
<test name="Simple example">
<groups>
<run>
<include name="checkintest"/>
<exclude name="broken"/>
</run>
</groups>
<classes>
<class name="example1.Test1"/>
</classes>
</test>
用这种方法,我将得到一个干净的测试运行,同时记录了那些被破坏并想要后续修复的测试。
注意:你也可以通过使用在@Test and @Before/After 注解上的"enabled"属性在个体的层面上关闭测试,
5.5 - Parial groups
你可以在类的级别上定义组,然后在方法的层次上添加组:
@Test(groups = { "checkin-test" })
public class All {
@Test(groups = { "func-test" )
public void method1() { ... }
public void method2() { ... }
}
在这个类中,method2() 属于在类的级别定义的"checkin-test"组,而method1() 同时属于 "checkin-test" 和 "func-test".
5.6 - Parameters
TestNG允许您在每个测试方法上使用任意数量的参数,通过@Parameters注解传递正确的参数。
有三种方式设置这些参数:
* 使用 testng.xml
* 以编程方式(Programmatically)
* java系统属性(Java system properties)
5.6.1 - Parameters from testng.xml
如果你使用简单的参数值,你可以在你的testng.xml中指定它们:
@Parameters({ "first-name" })
@Test
public void testSingleString(String firstName) {
System.out.println("Invoked testString " + firstName);
assert "Cedric".equals(firstName);
}
在这段代码中,我们指定Java方法的 firstName 参数应该接收名为 first-name 的 XML参数 的值。这个 XML参数 在testng.xml中定义。
<suite name="My suite">
<parameter name="first-name" value="Cedric"/>
<test name="Simple example">
<-- ... -->
可以用@Before/After和@Factory注释,实现同样的功能:
@Parameters({ "datasource", "jdbcDriver" })
@BeforeMethod
public void beforeTest(String ds, String driver) {
m_dataSource = ...; // look up the value of datasource
m_jdbcDriver = driver;
}
此时,两个Java参数 ds 和 driver 将分别接收配置文件给出的 datasource 和 jdbc-driver 的值。
参数可以通过@Optional 注解声明为可选:
@Parameters("db")
@Test
public void testNonExistentParameter(@Optional("mysql") String db) { ... }
如果在testng.xml文件中没有找到名为“db”的参数,你的测试方法将收到在 @Optional 注解中指定的默认值:“mysql”。
@Parameters 注解可以放在以下位置:
* 任何已经有@Test,@Before/After或@Factory注释的方法。
* 最多一个构造函数的测试类。在这种情况下,当需要实例化测试类时,TestNG将使用 Testng.xml 中指定的值初始化参数去调用这个特定的构造函数。这个特性可以用来将类中的字段初始化为测试方法要使用的值。
注意:
* XML参数按照在注释中找到它们的顺序映射到Java参数,如果不匹配,TestNG将发出错误。
* 参数是有作用范围的。在 testng.xml 中,您可以在<suite>标签下声明它们,也可以在<test>标签下声明它们。如果两个参数具有相同的名称,则 <test> 标签中定义的参数具有优先级。如果您需要指定一个适用于所有测试的参数,这是很方便的。
5.6.2 - Paramters with DataProviders
如果您需要传递复杂的参数,或者需要从Java中创建的参数(复杂对象、从属性文件或数据库中读取的对象等等),在testng.xml中指定参数可能是不够的。在这种情况下,您可以使用Data Provider来提供需要测试的值。Data Provider是类上的一个方法,它返回对象的数组的数组。这个方法是@DataProvider注解:
//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[][] {
{ "Cedric", new Integer(36) },
{ "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
@Test方法用 dataProvider 属性指定它的 DataProvider。该属性的名称必须与同一个类上用 @DataProvider(name="…") 注解的一个方法的 name 的传值一致。
默认情况下,将在当前测试类或其基类之一中查找 Data Provider。如果你想把你的数据提供到不同的类中,它需要是一个静态方法或一个带有非参数构造函数的类,并且你需要在 dataProviderClass 属性中指定可以找到它的类:
public class StaticProvider {
@DataProvider(name = "create")
public static Object[][] createData() {
return new Object[][] {
new Object[] { new Integer(42) }
};
}
}
public class MyTest {
@Test(dataProvider = "create", dataProviderClass = StaticProvider.class)
public void test(Integer n) {
// ...
}
}
Data provider 也支持注入。TestNG将使用测试上下文进行注入。Data Provider 的方法可以返回以下类型之一:
* 一个Object对象的二维数组(Object[][]),其中第一个维度的大小是调用测试方法的次数,第二个维度的大小包含一个必须与测试方法的参数类型兼容的对象数组。这就是上面的例子所说明的情况。
* 一个 Iterator<Object[]>。与Object[][]唯一的区别是Iterator允许您惰性地创建测试数据。TestNG将调用迭代器,然后调用带有该迭代器返回的参数的测试方法。如果您有很多参数集要传递给方法,而您不想在前面创建所有参数,那么这尤其有用。(dataProvider 需要是一个手写的迭代器)
# 一个Object对象的一位数组(Object[])。 这类似于 Iterator<Object[]> ,但会导致对源数组的每个元素调用一次测试方法。
# Iterator<Object>。Object[]的惰性替代。导致对迭代器的每个元素调用一次测试方法。
必须指出,返回类型不仅限于Object,MyCustomData[][] 或 Iterator< Supplier> 也是可能的。唯一的限制是,对于迭代器,它的形参类型本身不能显式参数化。下面是这个特性的一个例子:
@DataProvider(name = "test1")
public Iterator<Object[]> createData() {
return new MyIterator(DATA);
}
使用MyCustomData[]作为返回类型:
@DataProvider(name = "test1")
public MyCustomData[] createData() {
return new MyCustomData[]{ new MyCustomData() };
}
或者它的惰性选项 Iterator<MyCustomData>:
@DataProvider(name = "test1")
public Iterator<MyCustomData> createData() {
return Arrays.asList(new MyCustomData()).iterator();
}
Iterator的形参类型(Stream)不能显式参数化:
@DataProvider(name = "test1")
public Iterator<Stream> createData() {
return Arrays.asList(Stream.of("a", "b", "c")).iterator();
}
如果你在声明@DataProvider 以 java.lang.reflect.Method作为第一个参数,TestNG将传入测试方法作为 @DataProvider 方法的第一个参数的入参。当几个测试方法使用相同的@DataProvider,并且您希望它根据不同测试方法提供数据而返回不同的值时,这一点特别有用。
例如,下面的代码在它的@DataProvider中打印测试方法的名称:
@DataProvider(name = "dp")
public Object[][] createData(Method m) {
System.out.println(m.getName()); // print test method name
return new Object[][] { new Object[] { "Cedric" }};
}
@Test(dataProvider = "dp")
public void test1(String s) {
}
@Test(dataProvider = "dp")
public void test2(String s) {
}
将显示:
test1
test2
Data providers可以设置parallel属性并行运行:
@DataProvider(parallel = true)
// ...
从XML文件运行的并行数据提供程序(parallel data providers)共享相同的线程池,默认情况下线程池大小为10。你可以在XML文件的标签中修改这个值:
<suite name="Suite1" data-provider-thread-count="20" >
...
如果希望在不同的线程池中运行几个特定的数据提供程序,则需要从不同的XML文件运行它们。
5.6.3 - Parameters from System Properties
可以使用系统属性(-D),在Java虚拟机的命令行上传递TestNG参数。以这种方式传递的参数不需要在testng.xml中预定义,但会覆盖在那里定义的任何参数。
java -Dfirst-name=Cedrick -Dlast-name="von Braun" org.testng.TestNG testng.xml
Java系统属性变量是一个不带空格的字符串,用于表示属性的名称。值变量是一个表示属性值的字符串。如果该值是一个带空格的字符串,则用引号将其括起来。
注意:在TestNG 6.x中,TestNG.xml中定义的参数不能被系统属性覆盖。
5.6.4 - Patamters in reports
用于调用测试方法的参数会显示在TestNG生成的HTML报告中。下面是一个例子:
5.7 - Dependencies
有时,你需要以特定的顺序调用测试方法。以下是一些例子:
* 在运行更多的测试方法之前,确保一定数量的测试方法已经完成并成功。
* 初始化你的测试,同时希望这个初始化方法也成为测试方法(带有@Before/After标记的方法将不会成为最终报告的一部分)。
TestNG允许使用注释或XML指定依赖关系。
5.7.1 - Dependencies with annotations
你可以使用 @Test 注解的 dependsOnMethods 或 dependsOnGroups 属性。
它们有两种依赖关系:
* Hard dependencies(硬依赖关系)。所依赖的所有方法必须已经运行并成功运行。如果该测试方法依赖项中至少发生了一个故障,则不会在报告中调用该测试方法并将其标记为 SKIP。
* Soft dependencies(软依赖关系)。将始终在依赖的测试方法执行之后执行,即使其中一些依赖的测试方法已经执行失败。当你只想确保您的测试方法以特定的顺序运行,但它们的成功并不真正依赖于其他方法的成功时,这是很有用的。软依赖是通过在@Test注解中添加“alwaysRun=true”获得的。
以下是一个硬依赖的例子:
@Test
public void serverStartedOk() {}
@Test(dependsOnMethods = { "serverStartedOk" })
public void method1() {}
在这个例子中,method1()被声明为依赖于方法serverStartedOk(),这保证了serverStartedOk()总是首先被调用。
你也可以有依赖于整个组的方法:
@Test(groups = { "init" })
public void serverStartedOk() {}
@Test(groups = { "init" })
public void initEnvironment() {}
@Test(dependsOnGroups = { "init.*" })
public void method1() {}
在这个例子中,method1()声明为依赖于任何匹配正则表达式“init.*”的组,这保证了方法serverStartedOk()和initEnvironment()总是在method1()之前被调用。
注意:如前所述,在不同的测试运行中,不保证属于同一组的方法的调用顺序相同。
如果依赖的方法失败,并且您对它有硬依赖(alwaysRun=false,这是默认值),那么依赖它的方法不会标记为FAIL,而是标记为SKIP。跳过的方法将在最终报告中按原样报告(HTML中的颜色既不是红色也不是绿色),这一点很重要,因为跳过的方法不一定是失败的。
依赖项组和依赖项方法都接受正则表达式作为参数。对于dependsOnMethods,如果您所依赖的方法恰好具有多个重载版本,则会调用所有重载的方法。如果只想调用其中一个重载方法,则应该使用dependsOnGroups。
有关依赖方法的更高级示例,请参阅本文(官网外链不可用了),该文章使用继承为多个依赖关系问题提供了一个优雅的解决方案。
默认情况下,依赖方法按类分组。例如,如果方法b()依赖于方法a(),并且您有包含这些方法的类的多个实例(因为数据提供程序的工厂),那么调用顺序如下:
a(1)
a(2)
b(2) // 感觉这里应该是 b(1)
b(2)
TestNG将不会运行b(),直到所有实例都调用了它们的a()方法。
这种行为在某些情况下可能是不可取的,例如测试不同国家/地区的web浏览器的登录和注销。在这种情况下,您需要以下顺序:
signIn("us")
signOut("us")
signIn("uk")
signOut("uk")
对于这种排序,可以使用XML配置文件的 group-by-instances。该属性在<suite>或<test>上有效:
<suite name="Factory" group-by-instances="true">
or
<test name="Factory" group-by-instances="true">
5.7.2 - Dependencies in XML
你可以在testng.xml文件中指定您的组依赖关系。你可以使用 <dependncies> 标签来实现这一点:
<test name="My suite">
<groups>
<dependencies>
<group name="c" depends-on="a b" />
<group name="z" depends-on="c" />
</dependencies>
</groups>
</test>
<depends-on>属性包含一个空格分隔的组列表
5.8 -Fatories
工厂允许您动态地创建测试。例如,假设您想创建一个测试方法,该方法将多次访问Web站点上的一个页面,并且您想用不同的值调用它:(下面是错误实现场景的一个示例)
public class TestWebServer {
@Test(parameters = { "number-of-times" })
public void accessPage(int numberOfTimes) {
while (numberOfTimes-- > 0) {
// access the web page
}
}
}
<test name="T1">
<parameter name="number-of-times" value="10"/>
<classes>
<class name= "TestWebServer" />
</classes>
</test>
<test name="T2">
<parameter name="number-of-times" value="20"/>
<classes>
<class name= "TestWebServer"/>
</classes>
</test>
<test name="T3">
<parameter name="number-of-times" value="30"/>
<classes>
<class name= "TestWebServer"/>
</classes>
</test>
这很快就会变得无法管理,所以,你应该使用工厂:
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;
}
}
新的测试类现在是:
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
}
}
}
你的testng.xml只需要引用包含工厂方法的类,因为测试实例本身将在运行时创建:
<class name="WebTestFactory" />
或者,如果以编程方式构建测试套件实例,可以使用与测试相同的方式添加工厂:
TestNG testNG = new TestNG();
testNG.setTestClasses(WebTestFactory.class);
testNG.run();
工厂方法可以接收像 @Test 和 @Before/After 这样的参数,它必须返回 Object[]。返回的对象可以是任何类的(不一定与工厂类相同),它们甚至不需要包含TestNG注释(在这种情况下,它们将被TestNG忽略)。
工厂还可以与data providers,一起使用,您可以通过将@Factory注释放在常规方法或构造函数上来使用这一功能。下面是一个构造函数工厂的例子:
@Factory(dataProvider = "dp")
public FactoryDataProviderSampleTest(int n) {
super(n);
}
@DataProvider
static public Object[][] dp() {
return new Object[][] {
new Object[] { 41 },
new Object[] { 42 },
};
}
这个示例将让TestNG创建两个测试类,其中构造函数的值为41,另一个为42。
5.9 - Class level annotations
@Test注解可以放在类上而不仅仅是测试方法上:
@Test
public class Test1 {
public void test1() {
}
public void test2() {
}
}
类级@Test注释的作用是使该类的所有公共方法成为测试方法,即使它们没有注解。如果您想添加某些属性,您仍然可以在方法上使用@Test注释。
@Test
public class Test1 {
public void test1() {
}
@Test(groups = "g1")
public void test2() {
}
}
将生成test1()和test2()测试方法,但最重要的是,test2()现在属于组“g1”。
5.10 - Ignoring tests
TestNG允许你忽略所有的@Test的方法:
*在一个类中(或)
*在一个特定的包中(或)
*在一个包及其所有子包中
使用新的注解@Ignore
当在方法级别使用 @Ignore 注解时,在功能上等同于 @Test(enabled=false) 。下面的示例演示了如何忽略一个类中的所有测试。
import org.testng.annotations.Ignore;
import org.testng.annotations.Test;
@Ignore
public class TestcaseSample {
@Test
public void testMethod1() {
}
@Test
public void testMethod2() {
}
}
@Ignore 注解比单独的 @Test 方法注释具有更高的优先级。当在类上放置 @Ignore 时,该类中的所有测试将被禁用。
要忽略特定包中的所有测试,只需要创建 package-info.java 并向其添加 @Ignore 注解。这里有一个例子
@Ignore
package com.testng.master;
import org.testng.annotations.Ignore;