testng多线程并行执行测试

testng多线程并行执行测试

并行(多线程)技术在软件术语里被定义为软件、操作系统或者程序可以并行地执行另外一段程序中多个部分或者子组件的能力。TestNG允许我们以并行(多线程)的方式来执行测试。这就意味着基于TestNG测试组件的配置,多个线程可以被同时启动然后分别执行各自的测试方法。相对于传统的单线程执行测试的方式,这种多线程方式拥有很大的优势,主要是它可以减少测试运行时间,并且可以验证某段代码在多线程环境中运行的正确性。

invocationCount----表示执行的次数

threadPoolSize-----表示线程池的内线程的个数

timeOut-------超时时间-毫秒

目录


  1. 并行执行测试的优势
  2. 如何并行地执行测试方法
  3. 如何并行地执行测试类
  4. 如何并行地执行同一测试套件内的各个测试组件
  5. 如何配置需要在多线程环境中执行的测试方法

并行执行测试的优势

并行(多线程)执行测试可以给用户带来很多好处,主要包括以下两点:

1)减少了执行时间:并行测试也就意味着多个测试可以在同一时间被同时执行,从而减少了整体测试所花费的时间。

2)允许多个线程并行地测试同一个测试组件:有了这个特性,我们就能够写出相应的测试用例来验证应用程序中包含多线程部分的代码的正确性。

以上特性被广泛地应用在QA领域的自动化功能测试方面。通过简单的配置,QA人员就可以很轻松地使得他们的测试用例在多个浏览器或者操作系统中并行地执行。

TestNG提供了三种不同类型的配置方案来实现并行测试。

如何并行地执行测试方法

TestNG为我们提供了多种方式来实现并行测试,其中一种就是每一个独立的线程分别执行各自的测试方法。这种方式能够显著地减少测试执行时间,这是因为当有越多的测试方法被并行执行时,总体测试消耗时间将会越少。


1234567891011121314151617181920212223242526272829303132

​package​​​​com.howtodoinjava.parallelism;​


​import​​​​org.testng.annotations.AfterMethod;​​​​import​​​​org.testng.annotations.BeforeMethod;​​​​import​​​​org.testng.annotations.Test;​


​public​​​​class​​​​ParallelMethodTest​​​​{​​​​    ​​​​@BeforeMethod​​​​    ​​​​public​​​​void​​​​beforeMethod() {​​​​        ​​​​long​​​​id = Thread.currentThread().getId();​​​​        ​​​​System.out.println(​​​​"Before test-method. Thread id is: "​​​​+ id);​​​​    ​​​​}​


​    ​​​​@Test​​​​    ​​​​public​​​​void​​​​testMethodsOne() {​​​​        ​​​​long​​​​id = Thread.currentThread().getId();​​​​        ​​​​System.out.println(​​​​"Simple test-method One. Thread id is: "​​​​+ id);​​​​    ​​​​}​


​    ​​​​@Test​​​​    ​​​​public​​​​void​​​​testMethodsTwo() {​​​​        ​​​​long​​​​id = Thread.currentThread().getId();​​​​        ​​​​System.out.println(​​​​"Simple test-method Two. Thread id is: "​​​​+ id);​​​​    ​​​​}​


​    ​​​​@AfterMethod​​​​    ​​​​public​​​​void​​​​afterMethod() {​​​​        ​​​​long​​​​id = Thread.currentThread().getId();​​​​        ​​​​System.out.println(​​​​"After test-method. Thread id is: "​​​​+ id);​​​​    ​​​​}​​​​}​


上述测试类包含了两个测试方法,每个测试方法在执行时都会在控制台中打印出一条信息。每个测试方法以及它们各自的beforeMehod、afterMethod方法都会通过Thread.currentThread.getId()这段代码打印出执行该测试方法的线程的ID。

在项目中新建一个名为methods-test-testng.xml的文件并将下述代码写入该文件中。


1234567

​<​​​​suite​​​​name​​​​=​​​​"Test-method Suite"​​​​parallel​​​​=​​​​"methods"​​​​thread-count​​​​=​​​​"2"​​​​>​​​​  ​​​​<​​​​test​​​​name​​​​=​​​​"Test-method test"​​​​group-by-instances​​​​=​​​​"true"​​​​>​​​​    ​​​​<​​​​classes​​​​>​​​​      ​​​​<​​​​class​​​​name​​​​=​​​​"com.howtodoinjava.parallelism.ParallelMethodTest"​​​​/>​​​​    ​​​​</​​​​classes​​​​>​​​​  ​​​​</​​​​test​​​​>​​​​</​​​​suite​​​​>​


在Eclipse中选中该文件并且以TestNG测试套件方式运行它。你将会在控制台中看到以下输出信息:


123456

​Before test-method. Thread id is: ​​​​10​​​​Before test-method. Thread id is: ​​​​9​​​​Simple test-method Two. Thread id is: ​​​​10​​​​Simple test-method One. Thread id is: ​​​​9​​​​After test-method. Thread id is: ​​​​10​​​​After test-method. Thread id is: ​​​​9​


注意:上述截图中的ThreadId可能与你本地控制台中输出的ThreadId不同,这是因为ThreadId的值是在程序运行时由JVM动态指派的。

从上述测试结果中我们可以很清晰地看出:上述两个测试方法以及各自相应的beforeMethod和afterMethod方法是在两个独立的线程中执行的。我们可以通过在控制台中输出的ThreadId来证明这一点。

如何并行地执行测试类

在下个例子中,我们会说明如何并行地执行测试类:同一个测试组件(test execution)中的各个测试类将会在独立的线程中并行地执行。

ParallelClassesTestOne.java


1234567891011121314151617181920212223242526

​public​​​​class​​​​ParallelClassesTestOne​​​​{​​​​    ​​​​@BeforeClass​​​​    ​​​​public​​​​void​​​​beforeClass() {​​​​        ​​​​long​​​​id = Thread.currentThread().getId();​​​​        ​​​​System.out.println(​​​​"Before test-class. Thread id is: "​​​​+ id);​​​​    ​​​​}​


​    ​​​​@Test​​​​    ​​​​public​​​​void​​​​testMethodOne() {​​​​        ​​​​long​​​​id = Thread.currentThread().getId();​​​​        ​​​​System.out.println(​​​​"Sample test-method One. Thread id is: "​​​​+ id);​​​​    ​​​​}​


​    ​​​​@Test​​​​    ​​​​public​​​​void​​​​testMethodTwo() {​​​​        ​​​​long​​​​id = Thread.currentThread().getId();​​​​        ​​​​System.out.println(​​​​"Sample test-method Two. Thread id is: "​​​​+ id);​​​​    ​​​​}​


​    ​​​​@AfterClass​​​​    ​​​​public​​​​void​​​​afterClass() {​​​​        ​​​​long​​​​id = Thread.currentThread().getId();​​​​        ​​​​System.out.println(​​​​"After test-class. Thread id is: "​​​​+ id);​​​​    ​​​​}​​​​}​


ParallelClassesTestTwo.java


1234567891011121314151617181920212223242526

​public​​​​class​​​​ParallelClassesTestTwo​​​​{​​​​    ​​​​@BeforeClass​​​​    ​​​​public​​​​void​​​​beforeClass() {​​​​        ​​​​long​​​​id = Thread.currentThread().getId();​​​​        ​​​​System.out.println(​​​​"Before test-class. Thread id is: "​​​​+ id);​​​​    ​​​​}​


​    ​​​​@Test​​​​    ​​​​public​​​​void​​​​testMethodOne() {​​​​        ​​​​long​​​​id = Thread.currentThread().getId();​​​​        ​​​​System.out.println(​​​​"Sample test-method One. Thread id is: "​​​​+ id);​​​​    ​​​​}​


​    ​​​​@Test​​​​    ​​​​public​​​​void​​​​testMethodTwo() {​​​​        ​​​​long​​​​id = Thread.currentThread().getId();​​​​        ​​​​System.out.println(​​​​"Sample test-method Two. Thread id is: "​​​​+ id);​​​​    ​​​​}​


​    ​​​​@AfterClass​​​​    ​​​​public​​​​void​​​​afterClass() {​​​​        ​​​​long​​​​id = Thread.currentThread().getId();​​​​        ​​​​System.out.println(​​​​"After test-class. Thread id is: "​​​​+ id);​​​​    ​​​​}​​​​}​


在项目中新建一个名为classes-test-testng.xml的文件并将下述代码写入该文件中:


12345678

​<​​​​suite​​​​name​​​​=​​​​"Test-class Suite"​​​​parallel​​​​=​​​​"classes"​​​​thread-count​​​​=​​​​"2"​​​​>​​​​  ​​​​<​​​​test​​​​name​​​​=​​​​"Test-class test"​​​​>​​​​    ​​​​<​​​​classes​​​​>​​​​      ​​​​<​​​​class​​​​name​​​​=​​​​"com.howtodoinjava.parallelism.ParallelClassesTestOne"​​​​/>​​​​      ​​​​<​​​​class​​​​name​​​​=​​​​"com.howtodoinjava.parallelism.ParallelClassesTestTwo"​​​​/>​​​​    ​​​​</​​​​classes​​​​>​​​​  ​​​​</​​​​test​​​​>​​​​</​​​​suite​​​​>​

或者可以在测试用例的每一个被测的方法的注释@Test后面加上具体的线程信息如下:(只是举例并没有参与实际的eclipse运行)

 @Test(threadPoolSize = 2, invocationCount = 10, timeOut = 1000)

@BeforeClass(threadPoolSize = 2, invocationCount = 10, timeOut = 1000)
public void beforeClass() {
long id = Thread.currentThread().getId();
System.out.println("Before test-class. Thread id is: " + id);
}

@Test(threadPoolSize = 2, invocationCount = 10, timeOut = 1000)
public void testMethodOne() {
long id = Thread.currentThread().getId();
System.out.println("Sample test-method One. Thread id is: " + id);
}

@Test(threadPoolSize = 2, invocationCount = 10, timeOut = 1000)
public void testMethodTwo() {
long id = Thread.currentThread().getId();
System.out.println("Sample test-method Two. Thread id is: " + id);
}

@AfterClass(threadPoolSize = 2, invocationCount = 10, timeOut = 1000)
public void afterClass() {
long id = Thread.currentThread().getId();
System.out.println("After test-class. Thread id is: " + id);
}




在Eclipse中选中该文件并且以TestNG测试套件方式运行它。你将会在控制台中看到以下输出信息:


12345678

​Before test-​​​​class​​​​. Thread id is: ​​​​10​​​​Before test-​​​​class​​​​. Thread id is: ​​​​9​​​​Sample test-method One. Thread id is: ​​​​9​​​​Sample test-method One. Thread id is: ​​​​10​​​​Sample test-method Two. Thread id is: ​​​​10​​​​After test-​​​​class​​​​. Thread id is: ​​​​10​​​​Sample test-method Two. Thread id is: ​​​​9​​​​After test-​​​​class​​​​. Thread id is: ​​​​9​


从上述测试结果中我们可以很清晰地看出:上述两个测试类以及各自相应的beforeClass和afterClass方法是在独立的两个线程中执行的。我们可以通过在控制台中输出的ThreadId来证明这一点。

如何并行地执行同一测试套件内的各个测试组件

接下来我们会一起学习如何并行地执行同一个测试套件内的各个测试组件,即各个测试组件会分别在独立的线程中执行。


123456789101112131415161718192021222324252627282930313233343536373839404142434445464748

​package​​​​com.howtodoinjava.parallelism;​


​import​​​​org.testng.annotations.AfterClass;​​​​import​​​​org.testng.annotations.AfterTest;​​​​import​​​​org.testng.annotations.BeforeClass;​​​​import​​​​org.testng.annotations.BeforeTest;​​​​import​​​​org.testng.annotations.Parameters;​​​​import​​​​org.testng.annotations.Test;​


​public​​​​class​​​​ParallelSuiteTest​​​​{​​​​    ​​​​String testName = ​​​​""​​​​;​


​    ​​​​@BeforeTest​​​​    ​​​​@Parameters​​​​({ ​​​​"test-name"​​​​})​​​​    ​​​​public​​​​void​​​​beforeTest(String testName) {​​​​        ​​​​this​​​​.testName = testName;​​​​        ​​​​long​​​​id = Thread.currentThread().getId();​​​​        ​​​​System.out.println(​​​​"Before test "​​​​+ testName + ​​​​". Thread id is: "​​​​+ id);​​​​    ​​​​}​


​    ​​​​@BeforeClass​​​​    ​​​​public​​​​void​​​​beforeClass() {​​​​        ​​​​long​​​​id = Thread.currentThread().getId();​​​​        ​​​​System.out.println(​​​​"Before test-class "​​​​+ testName + ​​​​". Thread id is: "​​​​                ​​​​+ id);​​​​    ​​​​}​


​    ​​​​@Test​​​​    ​​​​public​​​​void​​​​testMethodOne() {​​​​        ​​​​long​​​​id = Thread.currentThread().getId();​​​​        ​​​​System.out.println(​​​​"Sample test-method "​​​​+ testName​​​​                ​​​​+ ​​​​". Thread id is: "​​​​+ id);​​​​    ​​​​}​


​    ​​​​@AfterClass​​​​    ​​​​public​​​​void​​​​afterClass() {​​​​        ​​​​long​​​​id = Thread.currentThread().getId();​​​​        ​​​​System.out.println(​​​​"After test-method  "​​​​+ testName​​​​                ​​​​+ ​​​​". Thread id is: "​​​​+ id);​​​​    ​​​​}​


​    ​​​​@AfterTest​​​​    ​​​​public​​​​void​​​​afterTest() {​​​​        ​​​​long​​​​id = Thread.currentThread().getId();​​​​        ​​​​System.out.println(​​​​"After test  "​​​​+ testName + ​​​​". Thread id is: "​​​​+ id);​​​​    ​​​​}​​​​}​


在项目中新建一个名为suite-test-testng.xml的文件并将以下代码写入该文件中:


1234567891011121314

​<​​​​suite​​​​name​​​​=​​​​"Test-class Suite"​​​​parallel​​​​=​​​​"tests"​​​​thread-count​​​​=​​​​"2"​​​​>​​​​    ​​​​<​​​​test​​​​name​​​​=​​​​"Test-class test 1"​​​​>​​​​        ​​​​<​​​​parameter​​​​name​​​​=​​​​"test-name"​​​​value​​​​=​​​​"test-method One"​​​​/>​​​​        ​​​​<​​​​classes​​​​>​​​​            ​​​​<​​​​class​​​​name​​​​=​​​​"com.howtodoinjava.parallelism.ParallelSuiteTest"​​​​/>​​​​        ​​​​</​​​​classes​​​​>​​​​    ​​​​</​​​​test​​​​>​​​​    ​​​​<​​​​test​​​​name​​​​=​​​​"Test-class test 2"​​​​>​​​​        ​​​​<​​​​parameter​​​​name​​​​=​​​​"test-name"​​​​value​​​​=​​​​"test-method One"​​​​/>​​​​        ​​​​<​​​​classes​​​​>​​​​            ​​​​<​​​​class​​​​name​​​​=​​​​"com.howtodoinjava.parallelism.ParallelSuiteTest"​​​​/>​​​​        ​​​​</​​​​classes​​​​>​​​​    ​​​​</​​​​test​​​​>​​​​</​​​​suite​​​​>​



在Eclipse中选中该文件并且以TestNG测试套件方式运行它。你将会在控制台中看到以下输出信息:


12345678910

​Before test Test One. Thread id is: ​​​​9​​​​Before test Test Two. Thread id is: ​​​​10​​​​Before test-​​​​class​​​​Test One. Thread id is: ​​​​9​​​​Before test-​​​​class​​​​Test Two. Thread id is: ​​​​10​​​​Sample test-method Test One. Thread id is: ​​​​9​​​​Sample test-method Test Two. Thread id is: ​​​​10​​​​After test-method  Test Two. Thread id is: ​​​​10​​​​After test-method  Test One. Thread id is: ​​​​9​​​​After test  Test One. Thread id is: ​​​​9​​​​After test  Test Two. Thread id is: ​​​​10​


从上述测试结果中我们可以很清晰地看出:上述两个测试组件是在独立的两个线程中分别执行的。我们可以通过在控制台中输出的ThreadId来证明这一点。

如何配置一个需要在多线程环境中执行的测试方法

之前我们讨论了如何并行(多线程)地执行测试方法,测试类以及测试组件。TestNG同时也提供了一种灵活的方式来配置需要在多线程环境下运行的测试方法:只要在该测试方法的@Test注解上配置一些信息,我们就能启用多线程模式。


123456789

​public​​​​class​​​​IndependentTest​​​​{​​​​    ​​​​@Test​​​​(threadPoolSize = ​​​​3​​​​, invocationCount = ​​​​6​​​​, timeOut = ​​​​1000​​​​)​​​​    ​​​​public​​​​void​​​​testMethod()​​​​    ​​​​{​​​​        ​​​​Long id = Thread.currentThread().getId();​​​​        ​​​​System.out.println(​​​​"Test method executing on thread with id: "​​​​+ id);​​​​    ​​​​}​​​​}​


上述测试方法是通过在@Test注解中配置threadPoolSize这个属性来进入多线程模式的。threadPoolSize被设为3,这就说明了该测试方法将会在三个不同的线程中同时执行。剩余两个属性:invocationCount配置的是该测试方法应被执行的总次数,timeOut配置的是每次执行该测试方法所耗费时间的阈值,超过阈值则测试失败。

在项目中新建一个名为independent-test-testng.xml的文件,并写入下述代码:


1234567

​<​​​​suite​​​​name​​​​=​​​​"Independent test Suite"​​​​>​​​​  ​​​​<​​​​test​​​​name​​​​=​​​​"Independent test"​​​​>​​​​    ​​​​<​​​​classes​​​​>​​​​     ​​​​<​​​​class​​​​name​​​​=​​​​"com.howtodoinjava.parallelism.IndependentTest"​​​​/>​​​​    ​​​​</​​​​classes​​​​>​​​​  ​​​​</​​​​test​​​​>​​​​</​​​​suite​​​​>​


在Eclipse中选中该文件并以TestNG测试套件方式运行。你将会在控制台中看到以下输出信息


123456

​Test method executing on thread with id: ​​​​11​​​​Test method executing on thread with id: ​​​​10​​​​Test method executing on thread with id: ​​​​9​​​​Test method executing on thread with id: ​​​​11​​​​Test method executing on thread with id: ​​​​11​​​​Test method executing on thread with id: ​​​​10​


在这里,我们可以看出该测试方法被执行了多次,而且它的执行次数跟invocationCount所指定的次数相同。而且从输出信息中,我们也可以很清晰地看到该测试方法每次执行都是在不同的线程中完成的。当我们需要按照某一固定次数,在多线程环境下运行某些测试方法时,上述特性会很有帮助,因为它避免了我们把整个测试套件都并行执行多次的代价。


test,class,method级别的并发,可以通过在testng.xml中的suite tag下设置,区别如下:
tests级别:不同test tag下的用例可以在不同的线程执行,相同test tag下的用例只能在同一个线程中执行。
classs级别:不同class tag下的用例可以在不同的线程执行,相同class tag下的用例只能在同一个线程中执行。
methods级别:所有用例都可以在不同的线程去执行。