走进Java接口测试之测试框架TestNG

一、简介

TestNG 是一个受 JUnit 和 NUnit 启发的测试框架,旨在简化广泛的测试需求,从单元测试到接口测试。
但引入了一些新功能,使其更强大,更易于使用,例如:

  • 注释。
  • 在线程池中运行测试,并提供各种可用策略(单线程,多线程等等)
  • 测试的代码是多线程安全的
  • 灵活的测试配置
  • 支持数据驱动的测试(使用 @DataProvider)
  • 支持参数化
  • 强大的执行模型(不再是 TestSuite)
  • 由各种工具和插件(Eclipse,IDEA,Maven 等)支持。
  • 嵌入 BeanShell 脚本以获得更大的灵活性
  • 用于运行时和日志记录的缺省 JDK 函数(无依赖项)
  • 应用服务器测试的依赖方法
  • TestNG旨在涵盖所有类别的测试:单元,接口,端到端,集成等......

二、快速 Demo

编写测试通常需要三个步骤:

  • 编写测试代码插入TestNG注释。
  • 在 testng.xml 中添加有关测试的信息(例如:类名,要运行的组等)。
  • 运行TestNG。

一个快速示例

package example1;

import org.testng.annotations.*;

public class SimpleTest {
 
 @BeforeClass
 public void setUp() {
   // 实例化时调用此代码
 }
 
 @Test(groups = { "fast" })
 public void aFastTest() {
   System.out.println("Fast test");
 }
 
 @Test(groups = { "slow" })
 public void aSlowTest() {
    System.out.println("Slow test");
 }
}

在构建测试类之后和运行任何测试方法之前,将调用 方法 setUp()。在这个例子中,我们将快速运行组,所以 aFastTest() 将被调用,而 aSlowTest() 将被跳过。

注意事项:

  • 无需扩展类或实现接口。
  • 尽管上面的示例使用了JUnit 约定,但我们的方法可以被称为任何名称,它是告诉 TestNG 它们是什么的注释。
  • 测试方法可以属于一个或多个组。
  • 将测试类编译到构建目录后,可以使用命令行,ant 任务(如下所示)或 XML 文件调用测试
<project default="test">
 
 <path id="cp">
   <pathelement location="lib/testng-testng-5.13.1.jar"/>
   <pathelement location="build"/>
 </path>

 <taskdef name="testng" classpathref="cp"
          classname="org.testng.TestNGAntTask" />
          
 <target name="test">
   <testng classpathref="cp" groups="fast">
     <classfileset dir="build" includes="example1/*.class"/>
   </testng>
 </target>
 
</project>

使用用 ant 调用它

c:> ant
Buildfile: build.xml
 
test:
[testng] Fast test
[testng] ===============================================
[testng] Suite for Command line test
[testng] Total tests run: 1, Failures: 0, Skips: 0
[testng] ===============================================
 
 
BUILD SUCCESSFUL
Total time: 4 seconds

然后,可以浏览测试结果:

start test-output\index.html (on Windows)

本文档中使用的概念如下:

  • 测试套件由一个 XML 文件表示。它可以包含一个或多个测试,并由 < suite > 标记定义。
  • 测试由 < test > 表示,可以包含一个或多个 TestNG 类。
  • TestNG 类是一个包含至少一个 TestNG 注释的 Java 类。它由 < class > 标记表示,可以包含一个或多个测试方法。
  • 测试方法是由源中的 @Test 注释的 Java 方法。

可以通过 @BeforeXXX 和 @AfterXXX 注释来配置 TestNG 测试,该注释允许在某个点之前和之后执行某些 Java 逻辑,这些点是上面列出的项目之一。

三、基本注释

以下是 TestNG 中可用注释及其属性的概述表。

注解 描述
@BeforeSuite 在该套件的所有测试都运行在注释的方法之前,仅运行一次。
@AfterSuite 在该套件的所有测试都运行在注释方法之后,仅运行一次。
@BeforeClass 在调用当前类的第一个测试方法之前运行,注释方法仅运行一次。
@AfterClass 在调用当前类的第一个测试方法之后运行,注释方法仅运行一次
@BeforeTest 注释的方法将在属于 < test > 标签内的类的所有测试方法运行之前运行。
@AfterTest 注释的方法将在属于< test >标签内的类的所有测试方法运行之后运行。
@BeforeGroups 配置方法将在之前运行组列表。 此方法保证在调用属于这些组中的任何一个的第一个测试方法之前不久运行。
@AfterGroups 此配置方法将在之后运行组列表。该方法保证在调用属于任何这些组的最后一个测试方法之后不久运行。
@BeforeMethod 注释方法将在每个测试方法之前运行。
@AfterMethod 注释方法将在每个测试方法之后运行。
alwaysRun 对于 before 方法(beforeSuite,beforeTest,beforeTestClass和beforeTestMethod,但不是beforeGroups):如果设置为true,则无论它属于哪个组,都将运行此配置方法。 对于after方法(afterSuite,afterClass,...):如果设置为true,即使先前调用的一个或多个方法失败或被跳过,也将运行此配置方法。
dependsOnGroups 此方法所依赖的组列表。
dependsOnMethods 此方法所依赖的方法列表。
enabled 是否启用此类 / 方法上的方法。
groups 此类/方法所属的组列表。
inheritGroups 如果为true,则此方法将属于类级别的@Test注释中指定的组。
onlyForGroups 仅适用于 @BeforeMethod 和 @AfterMethod。如果指定,则仅当相应的测试方法属于列出的组之一时,才会调用此 setup / teardown 方法。

当放置在 TestNG 类的超类上时,上述注释也将被继承。例如,这对于在公共超类中集中多个测试类的测试设置非常有用。

在这种情况下,TestNG保证“@Before”方法以继承顺序执行(首先是最高超类,然后是继承),而“@After”方法则按相反的顺序执行(向上继承链)。

@dataProvider: 将方法标记为为测试方法提供数据。
记一种方法来提供测试方法的数据。 注释方法必须返回一个Object [] [],其中每个Object [] 可以被分配给测试方法的参数列表。 要从该DataProvider接收数据的 @Test 方法需要使用与此注释名称相等的dataProvider名称。

属性 描述
name 此数据提供者的名称。如果未提供,则此数据提供程序的名称将自动设置为方法的名称。
parallel 如果设置为 true,则使用此数据提供程序生成的测试将并行运行。默认值为 false。

@Factory: 将方法标记为工厂,返回将由 TestNG 用作 Test 类的对象。该方法必须返回 Object []。

@Listeners :在测试类上定义侦听器。

属性 描述
value 扩展org.testng.ITestNGListener的类数组。

@Parameters: 描述如何将参数传递给 @Test 方法。

属性 描述
value 用于填充此方法参数的变量列表。

@Test : 将类或方法标记为测试的一部分。

属性 描述
alwaysRun 如果设置为 true,则即使依赖于失败的方法,也始终会运行此测试方法。
dataProvider 此测试方法的数据提供程序的名称。
dataProviderClass 查找数据提供程序的类。如果未指定,则将在当前测试方法的类或其基类之一上查找数据提供程序。如果指定了此属性,则数据提供程序方法必须在指定的类上是静态的。
dependsOnGroups 此方法所依赖的组列表。
dependsOnMethods 此方法所依赖的方法列表。
description 此方法的描述。
enabled 是否启用此类/方法上的方法。
expectedExceptions 预期测试方法抛出的异常列表。如果抛出此列表中没有异常或不同异常,则此测试将标记为失败。
groups 此类/方法所属的组列表。
invocationCount 应该调用此方法的次数。
invocationTimeOut 此测试应对所有调用计数的累计时间应采用的最大毫秒数。如果未指定 invocationCount,则将忽略此属性。
priority 此测试方法的优先级。将优先安排较低的优先事项。
successPercentage 此方法预期的成功百分比
singleThreaded 如果设置为 true,则此测试类上的所有方法都保证在同一个线程中运行,即使当前正在使用 parallel =“methods” 运行测试。此属性只能在类级别使用,如果在方法级别使用,它将被忽略。注意:此属性曾被称为顺序(现已弃用)。
timeOut 此测试应采用的最大毫秒数。
threadPoolSize 此方法的线程池大小。该方法将从 invocationCount 指定的多个线程调用。

注意:如果未指定 invocationCount,则忽略此属性

四、常用断言方法

为了方便判断测试用例是否执行成功,TestNG 特定提供了一个断言类,里面含有多种形式的断言方法。

方法 描述
assertTrue 判断是否为 true
assertFalse 判断是否为 false
assertSame 判断引用地址是否相同
assertNotSame 判断引用地址是否不相同
assertNull 判断是否为 null
assertNotNull 判断是否不为 null
assertEquals 判断是否相等,Object 类型的对象需要实现 hashCode 及 equals 方法
assertNotEquals 判断是否不相等
assertNoOrder 判断忽略顺序相等

五、testng.xml

您可以通过几种不同的方式调用 TestNG:

  • 使用 testng.xml 文件
  • ant
  • maven,如 mvn clean test -U -Dxml=xmlFileName
  • 命令行

本节介绍 testng.xml 的格式(您将在下面找到有关ant和命令行的文档)。

这是一个示例 testng.xml 文件

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
  
<suite name="Suite1" verbose="1" >
  <test name="Nopackage" >
    <classes>
       <class name="NoPackageTest" />
    </classes>
  </test>
 
  <test name="Regression1">
    <classes>
      <class name="test.sample.ParameterSample"/>
      <class name="test.sample.ParameterTest"/>
    </classes>
  </test>
</suite>

指定包名

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
 
<suite name="Suite1" verbose="1" >
  <test name="Regression1"   >
    <packages>
      <package name="test.sample" />
   </packages>
 </test>
</suite>

在此示例中,TestNG 将执行包 test.sample 中的所有类, 并仅保留具有 TestNG 注释的类。

指定要包含和排除的组和方法

<test name="Regression1">
  <groups>
    <run>
      <exclude name="brokenTests"  />
      <include name="checkinTests"  />
    </run>
  </groups>
  
  <classes>
    <class name="test.IndividualMethodsTest">
      <methods>
        <include name="testMethod" />
      </methods>
    </class>
  </classes>
</test>

在 testng.xml 中定义新组,并在属性中指定其他详细信息,例如是否并行运行测试,使用多少线程,是否运行测试等等...
默认情况下,TestNG 将按照 XML 文件中的顺序运行测试。如果希望此文件中列出的类和方法以不可预测的顺序运行,请将 preserve-order 属性设置为 false

<test name="Regression1" preserve-order="false">
  <classes>
 
    <class name="test.Test1">
      <methods>
        <include name="m1" />
        <include name="m2" />
      </methods>
    </class>
 
    <class name="test.Test2" />
 
  </classes>
</test>

六、测试方法,测试类和测试组

1、测试方法

测试方法用 @Test 注释。除非在 testng.xml 中将 allow-return-values 设置为 true,否则将忽略使用 @Test 注释恰好返回值的方法:

<suite allow-return-values="true">
or
<test allow-return-values="true">

2、测试组

TestNG 允许执行复杂的测试方法分组。不仅可以声明方法属于组,还可以指定包含其他组的组。然后可以调用 TestNG 并要求包括一组特定的组(或正则表达式),同时排除另一组。这为分区测试提供了最大的灵活性,如果想要连续运行两组不同的测试,则不需要重新编译任何内容。

组在 testng.xml 文件中指定,可以在 < test > 或 < suite > 标记下找到。标记中指定的组适用于下面的所有 < test > 标记。请注意,组在这些标记中是累积的:如果在< suite >中指定组 “a”,在< test >中指定“b” ,则将包括“a”和“b”。

例如,至少有两类测试是很常见的

  • 办理登机手续的测试。应在提交新代码之前运行这些测试。它们通常应该很快,并确保没有基本功能被破坏。
  • 功能测试。这些测试应涵盖软件的所有功能,并且每天至少运行一次,尽管理想情况下希望连续运行它们。

通常,签入测试是功能测试的子集。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>

将运行该类中的所有测试方法,而使用 checkintest 调用它将只运行 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

方法组
还可以排除或包含单个方法

<test name="Test1">
  <classes>
    <class name="example1.Test1">
      <methods>
        <include name=".*enabledTestMethod.*"/>
        <exclude name=".*brokenTestMethod.*"/>
      </methods>
     </class>
  </classes>
</test>

这可以派上用来停用单个方法而不必重新编译任何东西,但是不建议过多地使用这种技术,因为如果开始重构你的 Java 代码(正则表达式中使用的正则表达式),它会使你的测试框架崩溃。标签可能不再符合您的方法)。

3、群组

组还可以包括其他组。这些组称为 “MetaGroups”。例如,您可能希望定义包含 “checkintest” 和 “functest” 的组 “all”。“functest” 本身将包含 “windows” 和 “linux” 组,而 “checkintest” 将只包含 ”windows“。以下是如何在属性文件中定义它:

<test name="Regression1">
  <groups>
    <define name="functest">
      <include name="windows"/>
      <include name="linux"/>
    </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>

4、排除组

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 和 @Before / After 注释上的 “enabled” 属性逐个禁用测试。

5、部分组

可以在类级别定义组,然后在方法级别添加组:

@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”。

七、参数化

测试方法不必是无参数的。可以在每个测试方法上使用任意数量的参数,并指示 TestNG 使用 @Parameters 注释传递正确的参数。

有两种方法可以设置这些参数:

  • 使用 testng.xml
  • 以编程方式。

1、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 = ...;                              // 查询数据源值
  m_jdbcDriver = driver;
}

这次,两个 Java 参数 ds 和驱动程序将分别接收赋予属性 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 >中定义的具有优先权的参数。如果您需要指定适用于所有测试的参数并仅为某些测试覆盖其值,这将非常方便。

2、使用 DataProviders 的参数

如果需要传递复杂参数或需要从 Java 创建的参数(复杂对象,从属性文件或数据库读取的对象等等),则在 testng.xml 中指定参数可能不够。在这种情况下,您可以使用数据提供程序提供测试所需的值。数据提供程序是类上的一个方法,它返回一组对象数组。此方法使用 @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(name =“...”)注释的同一类上的方法。
默认情况下,将在当前测试类或其中一个基类中查找数据提供程序。如果要将数据提供程序放在不同的类中,则需要使用静态方法或具有非 arg 构造函数的类,并指定可在 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) {
    // ...
  }
}

数据提供者也支持注入。TestNG 将使用测试上下文进行注射。Data Provider方法可以返回以下两种类型之一:
一组对象数组(Object [] []),其中第一个维度的大小是调用测试方法的次数,第二个维度大小包含必须与测试的参数类型兼容的对象数组方法。这是上述示例所示的情况。
一个迭代< Object [] [] > 。与Object [] []的唯一区别在于 Iterator 允许您懒惰地创建测试数据。TestNG 将调用迭代器,然后使用此迭代器返回的参数逐个调用测试方法。如果您有许多参数集要传递给方法,并且您不想预先创建所有参数集,则此功能特别有用。
以下是此功能的示例:

@DataProvider(name = "test1")
public Iterator<Object[]> createData() {
  return new MyIterator(DATA);
}

如果您将 @DataProvider 声明为将 java.lang.reflect.Method 作为第一个参数,则 TestNG 将为此第一个参数传递当前测试方法。当多个测试方法使用相同的 @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

数据提供程序可以与并行属性并行运行:

@DataProvider(parallel = true)
// ...

从 XML 文件运行的并行数据提供程序共享相同的线程池,默认情况下大小为 10。您可以在 XML 文件的< suite >标记中修改此值:

<suite name="Suite1" data-provider-thread-count="20" >

如果要在不同的线程池中运行几个特定的​​数据提供程序,则需要从其他XML文件运行它们。

八、依赖性

有时,您需要按特定顺序调用测试方法。这里有一些例子:
在运行更多测试方法之前,确保已完成并成功执行一定数量的测试方法。
要初始化测试,同时希望这个初始化方法也是测试方法(使用 @Before / After 标记的方法不会成为最终报告的一部分)。
TestNG 允许您使用注释或 XML 指定依赖项。

1、带注释的依赖关系

您可以使用属性 dependsOnMethods 或 dependsOnGroups,对发现的@Test注解。

有两种依赖关系:

  • 硬依赖。您依赖的所有方法必须运行并成功运行。如果您的依赖项中至少发生一次故障,则不会在报告中调用并将其标记为 SKIP。
  • 软依赖。您将始终在您依赖的方法之后运行,即使其中一些方法失败了。当您只是想确保您的测试方法按特定顺序运行时,这很有用,但它们的成功并不真正取决于其他人的成功。通过在 @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中颜色既不是红色也不是绿色),这很重要,因为跳过的方法不一定是失败的。

无论 dependsOnGroups 和 dependsOnMethods 接受正则表达式作为参数。对于 dependsOnMethods,如果您依赖于碰巧有多个重载版本的方法,则将调用所有重载的方法。如果您只想调用其中一个重载方法,则应使用 dependsOnGroups。

默认情况下,依赖方法按类分组。例如,如果方法b()依赖于方法a(),并且您有几个包含这些方法的类的实例(因为数据提供程序的工厂),则调用顺序如下:

a(1)
a(2)
b(2)
b(2)

在所有实例调用其 a() 方法之前, TestNG 不会运行 b() 。
在某些情况下可能不希望出现这种情况,例如测试登录和退出各个国家/地区的Web浏览器。在这种情况下,您需要以下订购:

signIn("us")
signOut("us")
signIn("uk")
signOut("uk")

对于此排序,可以逐个实例使用 XML 属性。此属性在 < suite > 或 < test > 上有效:

  <suite name="Factory" group-by-instances="true">
or
  <test name="Factory" group-by-instances="true">

2、XML 中的依赖关系

可以在 testng.xml 文件中指定组依赖项。使用 < dependencies > 标记来实现此目的:

<test name="My suite">
  <groups>
    <dependencies>
      <group name="c" depends-on="a  b" />
      <group name="z" depends-on="c" />
    </dependencies>
  </groups>
</test>

以上所述 < depends-on > 属性包含一个以空格分隔的组列表。

九、工厂

工厂允许动态创建测试。
例如,假设要创建一个将多次访问网站上的页面的测试方法,并且希望使用不同的值调用它:

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 忽略)。

工厂也可以与数据提供者一起使用,您可以通过将 @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。

十、忽略测试

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;

这会导致 com.testng.master 包及其所有子包中的所有 @Test 方法都被忽略。

十一、并行和超时

可以指示 TestNG 以各种方式在单独的线程中运行测试。

1、 并行套件

如果您运行多个套件文件(例如“ java org.testng.TestNG testng1.xml testng2.xml”)并且希望每个套件都在一个单独的线程中运行,这将非常有用。您可以使用以下命令行标志来指定线程池的大小:

java org.testng.TestNG -suitethreadpoolsize 3 testng1.xml testng2.xml testng3.xml

相应的 ant 任务名称是 suitethreadpoolsize。

2、并行测试,类和方法

在平行于 < suite > 属性标签可以采取以下值之一:

<suite name="My suite" parallel="methods" thread-count="5">
<suite name="My suite" parallel="tests" thread-count="5">
<suite name="My suite" parallel="classes" thread-count="5">
<suite name="My suite" parallel="instances" thread-count="5">
  • parallel =“methods”:TestNG将在不同的线程中运行所有测试方法。依赖方法也将在单独的线程中运行,但它们将遵循您指定的顺序。
  • parallel =“tests”:TestNG将在同一个线程中运行相同 < test > 标记中的所有方法,但每个< test > 标记将位于一个单独的线程中。这允许您在同一个 < test > 中对所有非线程安全的类进行分组,并保证它们将在同一个线程中运行,同时利用 TestNG 使用尽可能多的线程来运行测试。
  • parallel =“classes”:TestNG 将在同一个线程中运行同一个类中的所有方法,但每个类将在一个单独的线程中运行
  • parallel =“instances”:TestNG 将在同一个线程中运行同一实例中的所有方法,但两个不同实例上的两个方法将在不同的线程中运行。

此外,属性 thread-count 允许指定应为此执行分配的线程数。
注意:@Test 属性 timeOut 在并行和非并行模式下都有效。

您还可以指定应从不同的线程调用 @Test 方法。您可以使用 threadPoolSize 属性来实现此结果:

@Test(threadPoolSize = 3, invocationCount = 10,  timeOut = 10000)
public void testServer() {

在此示例中,函数 testServer 将从三个不同的线程调用十次。此外,超时十秒可确保所有线程都不会永久阻塞此线程。

十二、重新运行失败的测试

每次测试在套件中失败时,TestNG 都会在输出目录中创建一个名为 testng-failed.xml 的文件。此 XML 文件包含仅重新运行失败的这些方法的必要信息,使您可以快速重现故障,而无需运行整个测试。因此,典型的会话将如下所示:

java -classpath testng.jar;%CLASSPATH% org.testng.TestNG -d test-outputs testng.xml
java -classpath testng.jar;%CLASSPATH% org.testng.TestNG -d test-outputs test-outputs\testng-failed.xml

请注意,testng-failed.xml 将包含所有必需的依赖方法,以便您可以保证在没有任何 SKIP 故障的情况下运行失败的方法。

有时,您可能希望TestNG在失败时自动重试测试。在这些情况下,您可以使用重试分析器。当您将重试分析器绑定到测试时,TestNG会自动调用重试分析器以确定TestNG是否可以再次重试测试用例,以尝试查看现在刚刚失败的测试是否通过。以下是使用重试分析器的方法:
构建 org.testng.IRetryAnalyzer 接口的实现
将此实现绑定到 @Test 注释,例如 @Test(retryAnalyzer = LocalRetry.class)
以下是重试分析器的示例实现,重试测试最多三次。

import org.testng.IRetryAnalyzer;
import org.testng.ITestResult;
 
public class MyRetry implements IRetryAnalyzer {
 
  private int retryCount = 0;
  private static final int maxRetryCount = 3;
 
  @Override
  public boolean retry(ITestResult result) {
    if (retryCount < maxRetryCount) {
      retryCount++;
      return true;
    }
    return false;
  }
}
import org.testng.Assert;
import org.testng.annotations.Test;
 
public class TestclassSample {
 
  @Test(retryAnalyzer = MyRetry.class)
  public void test2() {
    Assert.fail();
  }
}

十三、YAML文件

TestNG 支持 YAML作为指定套件文件的替代方法。例如,以下 XML 文件:

<suite name="SingleSuite" verbose="2" thread-count="4">
 
  <parameter name="n" value="42" />
 
  <test name="Regression2">
    <groups>
      <run>
        <exclude name="broken" />
      </run>
    </groups>
 
    <classes>
      <class name="test.listeners.ResultEndMillisTest" />
    </classes>
  </test>
</suite>

这是它的YAML版本:

name: SingleSuite
threadCount: 4
parameters: { n: 42 }
 
tests:
  - name: Regression2
    parameters: { count: 10 }
    excludedGroups: [ broken ]
    classes:
      - test.listeners.ResultEndMillisTest

这是 TestNG 自己的套件文件,以及它的 YAML 对应文件。
您可能会发现 YAML 文件格式更易于阅读和维护。TestNG Eclipse 插件也可以识别 YAML 文件。

注意:
默认情况下,TestNG 不会将 YAML 相关库引入您的类路径。因此,根据您的构建系统(Gradle / Maven),您需要在构建文件中添加对 YAML 库的显式引用。

例如,如果使用的是 Maven,则需要在pom.xml文件中添加如下依赖项:

<dependency>
  <groupid>org.yaml</groupid>
  <artifactid>snakeyaml</artifactid>
  <version>1.23</version>
</dependency>

参考资料:
[1] https://testng.org/doc/index.html

上一篇:走进Java接口测试之Mock(概念篇)


下一篇:如何使用 SQL 对数据进行分析?