【转】TestNG系列之--03 多线程
1. 多线程--并行执行测试的优势
在软件术语里:并行(多线程)技术被定义为软件、操作系统或者程序可以并行地执行另外一段程序中多个部分或者子组件的能力
多线程可以给用户带来很多好处,主要包括以下两点:
1)减少执行时间:并行测试也就意味着多个测试可以在同一时间被同时执行,从而减少了整体测试所花费的时间。
2)允许多个线程并行地测试同一个测试组件:有了这个特性,我们就能够写出相应的测试用例来验证应用程序中包含多线程部分的代码的正确性。
以上特性被广泛地应用在QA领域的自动化功能测试方面。通过简单的配置,QA人员就可以很轻松地使得他们的测试用例在多个浏览器或者操作系统中并行地执行。
2. TestNG中的多线程使用姿势
2.1 如何并行地执行
---------------------------------------- suite ----------------------------------------------
一般情况下,一个testng.xml只包含一个suite。如果想起多个线程执行不同的suite,官方给出的方法是:通过命令行的方式来指定线程池的容量。
java org.testng.TestNG -suitethreadpoolsize 3 testng1.xml testng2.xml testng3.xml
即可通过三个线程来分别执行testng1.xml、testng2.xml、testng3.xml。
实际上这种情况在实际中应用地并不多见,我们的测试用例往往放在一个suite中,如果真需要执行不同的suite,往往也是在不同的环境中去执行,届时也自然而然会做一些其他的配置(如环境变量)更改,会有不同的进程去执行。因此这种方式不多赘述。
-----------------------------------test, class, method ----------------------------------------------
可以通过在testng.xml中的suite tag下设置,如:
<suite name="Testng Parallel Test" parallel="tests" thread-count="5">
<suite name="Testng Parallel Test" parallel="classes" thread-count="5">
<suite name="Testng Parallel Test" parallel="methods" thread-count="5">
它们的共同点都是最多起5个线程去同时执行不同的用例。
它们的区别如下:
- tests级别:不同test tag下的用例可以在不同的线程执行,相同test tag下的用例只能在同一个线程中执行。
- classs级别:不同class tag下的用例可以在不同的线程执行,相同class tag下的用例只能在同一个线程中执行。
- methods级别:所有用例都可以在不同的线程去执行。
搞清楚并发的级别非常重要,可以帮我们合理地组织用例,比如将非线程安全的测试类或group统一放到一个test中,这样在并发的同时又可以保证这些类里的用例是单线程执行。
2.2 并发时的依赖
实践中,很多时候我们在测试类中通过dependOnMethods/dependOnGroups方式,给很多测试方法的执行添加了依赖,以达到期望的执行顺序。如果同时在运行testng时配置了methods级别并发执行,那么这些测试方法在不同线程中执行,还会遵循依赖的执行顺序吗?
答案是——YES。牛逼的TestNG就是能在多线程情况下依然遵循既定的用例执行顺序去执行。
2.3 不同dataprovider的并发
在使用TestNG做自动化测试时,基本上大家都会使用dataprovider来管理一个用例的不同测试数据。而上述在testng.xml中修改suite标签的方法,并不适用于dataprovider多组测试数据之间的并发。执行时会发现,一个dp中的多组数据依然是顺序执行。
解决方式是:在 @DataProvider中添加parallel=true。示例代码如下:
import org.testng.annotations.DataProvider;
import testdata.ScenarioTestData;
public class ScenarioDataProvider {
@DataProvider(name = "hadoopTest", parallel=true)
public static Object [][] hadoopTest(){
return new Object[][]{
ScenarioTestData.hadoopMain,
ScenarioTestData.hadoopRun,
ScenarioTestData.hadoopDeliverProps
};
}
@DataProvider(name = "sparkTest", parallel=true)
public static Object [][] sparkTest(){
return new Object[][]{
ScenarioTestData.spark_java_version_default,
ScenarioTestData.spark_java_version_162,
ScenarioTestData.spark_java_version_200,
ScenarioTestData.spark_python
};
}
@DataProvider(name = "sqoopTest", parallel=true)
public static Object [][] sqoopTest(){
return new Object[][]{
ScenarioTestData.sqoop_mysql2hive,
ScenarioTestData.sqoop_mysql2hdfs
};
}
}
默认情况下,dp并行执行的线程池容量为10,如果要更改并发的数量,也可以在suite tag下指定参数data-provider-thread-count:
<suite name="Testng Parallel Test" parallel="methods" thread-count="5" data-provider-thread-count="20" >
2.4 同一个方法的并发
有些时候,我们需要对一个测试用例,比如一个http接口,执行并发测试,即一个接口的反复调用。
TestNG中也提供了优雅的支持方式,在 @Test标签中指定threadPoolSize和invocationCount。
@Test(enabled=true, dataProvider="testdp", threadPoolSize=5, invocationCount=10)
其中threadPoolSize表明用于调用该方法的线程池容量,该例就是同时起5个线程并行执行该方法;invocationCount表示该方法总计需要被执行的次数。该例子中5个线程同时执行,当总计执行次数达到10次时,停止。
注意,该线程池与dp的并发线程池是两个独立的线程池。这里的线程池是用于起多个method,而每个method的测试数据由dp提供,如果这边dp里有3组数据,那么实际上10次执行,每次都会调3次接口,这个接口被调用的总次数是10*3=30次。threadPoolSize指定的5个线程中,每个线程单独去调method时,用到的dp如果也是支持并发执行的话,会创建一个新的线程池(dpThreadPool)来并发执行测试数据。
示例代码如下:
package testng.parallel.test;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class TestClass1 {
private SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
@BeforeClass
public void beforeClass(){
System.out.println("Start Time: " + df.format(new Date()));
}
@Test(enabled=true, dataProvider="testdp", threadPoolSize=2, invocationCount=5)
public void test(String dpNumber) throws InterruptedException{
System.out.println("Current Thread Id: " + Thread.currentThread().getId() + ". Dataprovider number: "+ dpNumber);
Thread.sleep(5000);
}
@DataProvider(name = "testdp", parallel = true)
public static Object[][]testdp(){
return new Object[][]{
{"1"},
{"2"}
};
}
@AfterClass
public void afterClass(){
System.out.println("End Time: " + df.format(new Date()));
}
}
测试结果:
Start Time: 2017-03-11 14:10:43
[ThreadUtil] Starting executor timeOut:0ms workers:5 threadPoolSize:2
Current Thread Id: 14. Dataprovider number: 2
Current Thread Id: 15. Dataprovider number: 2
Current Thread Id: 12. Dataprovider number: 1
Current Thread Id: 13. Dataprovider number: 1
Current Thread Id: 16. Dataprovider number: 1
Current Thread Id: 18. Dataprovider number: 1
Current Thread Id: 17. Dataprovider number: 2
Current Thread Id: 19. Dataprovider number: 2
Current Thread Id: 21. Dataprovider number: 2
Current Thread Id: 20. Dataprovider number: 1
End Time: 2017-03-11 14:10:58
2.5 测试类中,singleThreaded=true属性
如果@Test使用了singleThreaded=true属性,则该测试类的所有测试方法都只能在单线程中执行,无论其他设置如何。示例代码如下:
@Test(singleThreaded=true)
public class Test1 {
public void test1() {
}
public void test2() {
}
}
总结
在TestNG中使用多线程的方式并行执行测试用例可以有效提高用例的执行速度,而且TestNG对多线程提供了很好的支持,即使是菜鸟也可以方便地上手多线程。此外,TestNG默认会使用线程池的方式创建线程,减小了程序的开销。