优势

并行(多线程)技术在软件术语里被定义为软件、操作系统或者程序可以并行地执行另外一段程序中多个部分或者子组件的能力

多线程方式拥有很大的优势:

1). 减少测试运行时间

  如果测试集里包含了大量的用例时,多线程方式提高执行速度

2). 验证某段代码在多线程环境中运行的正确性。

基于上述场景,我们可以考虑将自动化用例中相互之间没有耦合关系,相对独立的用例进行并行执行。

  必须要指出的是,我们在设计用例时就要考虑到用例是否适合并发执行,要注意多线程方式的通病:线程安全与共享变量的问题。建议是在测试代码中,尽可能地避免使用共享变量。如果真的用到了,要慎用synchronized关键字来对共享变量进行加锁同步。否则,难免你的用例执行时可能会出现不稳定的情景(经常听到有人提到用例执行地不稳定,有时100%通过,有时只有90%通过,猜测可能有一部分原因也是这个导致的)。

TestNG中的多线程使用姿势

不同级别的并发

通常,在TestNG的执行中,测试的级别由上至下可以分为suite -> test -> class -> method,箭头的左边元素跟右边元素的关系是一对多的包含关系。

这里的test指的是testng.xml中的test tag,而不是测试类里的一个 @Test。测试类里的一个 @Test实际上对应这里的method。所以我们在使用 @BeforeSuite、 @BeforeTest、 @BeforeClass、 @BeforeMethod这些标签的时候,它们的实际执行顺序也是按照这个级别来的。

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

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中,这样在并发的同时又可以保证这些类里的用例是单线程执行。也可以根据需要设定class级别的并发,让同一个测试类里的用例在同一个线程中执行。

并发时的依赖

实践中,很多时候我们在测试类中通过dependOnMethods/dependOnGroups方式,给很多测试方法的执行添加了依赖,以达到期望的执行顺序。如果同时在运行testng时配置了methods级别并发执行,那么这些测试方法在不同线程中执行,还会遵循依赖的执行顺序吗?答案是——YES。牛逼的TestNG就是能在多线程情况下依然遵循既定的用例执行顺序去执行。

不同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" >

同一个方法的并发

有些时候,我们需要对一个测试用例,比如一个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

测试类中,singleThreaded=true属性

如果@Test使用了singleThreaded=true属性,则该测试类的所有测试方法都只能在单线程中执行,无论其他设置如何。示例如下:

  1. @Test(singleThreaded=true)
  2. public class Test1 {
  3. public void test1() {
  4. }
  5. public void test2() {
  6. }
  7. }

Other TestNG Tips

TestNG作为一个成熟的、业界广泛使用的测试框架,自然有其存在的合理性。这边再分享一些简单有用的标签,具体的使用姿势大家可以自己去探索,官网有比较全的介绍,毕竟自己探索的才会印象深刻。

  1. groups/dependsOnGroups/dependsOnMethods ——设置用例间依赖
  2. dataProviderClass ——将dataprovider单独放到一个专用的类中,实现测试代码、dataprovider、测试数据分层。
  3. timeout ——设置用例的超时时间(并发/非并发都可支持)
  4. alwaysRun ——某些依赖的用例失败了,导致用例被跳过。对于一些为了保持环境干净而“扫尾”的测试类,如果我们想强制执行可以使用此标签。
  5. priority ——设置优先级,让某些测试用例被更大概率优先执行。
  6. singleThreaded ——强制一个class类里的用例在一个线程执行,忽视method级别并发
  7. preserve-order ——指定是否按照testng.xml中的既定用例顺序执行用例

总结

在TestNG中使用多线程的方式并行执行测试用例可以有效提高用例的执行速度,而且TestNG对多线程提供了很好的支持,即使是菜鸟也可以方便地上手多线程。此外,TestNG默认会使用线程池的方式创建线程,减小了程序的开销。

参考链接

【转】TestNG中的并发(多线程)的更多相关文章

  1. 简单聊聊TestNG中的并发

    前言 最近在做项目里的自动化测试工作,使用的是TestNG测试框架,主要涉及的测试类型有接口测试以及基于业务实际场景的场景化测试.由于涉及的场景大多都是大数据的作业开发及执行(如MapReduce.S ...

  2. python编程中的并发------多线程threading模块

    任务例子:喝水.吃饭动作需要耗时1S 单任务:(耗时20s) for i in range(10): print('a正在喝水') time.sleep(1) print('a正在吃饭') time. ...

  3. (五)TestNG测试的并发执行详解

    原文链接:https://blog.csdn.net/taiyangdao/article/details/52159065 TestNG在执行测试时,默认suitethreadpoolsize=1, ...

  4. C#中实现并发的几种方法的性能测试

    C#中实现并发的几种方法的性能测试 0x00 起因 去年写的一个程序因为需要在局域网发送消息支持一些命令和简单数据的传输,所以写了一个C/S的通信模块.当时的做法很简单,服务端等待链接,有用户接入后开 ...

  5. 深入解析PHP中的(伪)多线程与多进程

    本篇文章是对PHP中的(伪)多线程与多进程进行了详细的分析介绍,需要的朋友参考下 (伪)多线程:借助外力利用WEB服务器本身的多线程来处理,从WEB服务器多次调用我们需要实现多线程的程序.QUOTE: ...

  6. WCF初探-28:WCF中的并发

    理解WCF中的并发机制 在对WCF并发机制进行理解时,必须对WCF初探-27:WCF中的实例化进行理解,因为WCF中的并发特点是伴随着服务实例上下文实现的.WCF的实例上下文模型可以通过Instanc ...

  7. Python中的并发编程

    简介 我们将一个正在运行的程序称为进程.每个进程都有它自己的系统状态,包含内存状态.打开文件列表.追踪指令执行情况的程序指针以及一个保存局部变量的调用栈.通常情况下,一个进程依照一个单序列控制流顺序执 ...

  8. C#中异步和多线程的区别

    C#中异步和多线程的区别是什么呢?异步和多线程两者都可以达到避免调用线程阻塞的目的,从而提高软件的可响应性.甚至有些时候我们就认为异步和多线程是等同的概念.但是,异步和多线程还是有一些区别的.而这些区 ...

  9. C#中实现并发

    C#中实现并发的几种方法的性能测试 0x00 起因 去年写的一个程序因为需要在局域网发送消息支持一些命令和简单数据的传输,所以写了一个C/S的通信模块.当时的做法很简单,服务端等待链接,有用户接入后开 ...

随机推荐

  1. Ubuntu 18的网络配置

    包括Ubuntu 18.04和18.10,设置为静态IP及DNS. sudo vim /etc/netplan/50-cloud-init.yaml network: ethernets: enp4s ...

  2. ajax从入门到深入精通

    前言 ajax全称Asynchronous Javascript and XML.其中Asynchronous代表异步.同步于异步是描述通信模式的概念,同步机制:发送方发生请求后,需要等待接收到接收方 ...

  3. mongdb数据库的操作

    一.数据库使用 1.使用mongodb服务,必须先开启服务,开启服务使用 mongod --dbpath D:mongdb    (D:mongdb  自己所创建数据库的路径, 在cmd窗口中输入) ...

  4. CF633H Fibonacci-ish II

    题目描述 题解: 坑题搞了三天. 莫队+线段树. 还有一些和斐波那契数列有关的性质. 首先答案是$a_1f_1+a_2f_2+…+a_nf_n$, 考虑插进去一个元素对答案产生的影响. 比如插进去一个 ...

  5. Python中 模块、包、库

    模块:就是.py文件,里面定义了一些函数和变量,需要的时候就可以导入这些模块. 包:在模块之上的概念,为了方便管理而将文件进行打包.包目录下第一个文件便是 __init__.py,然后是一些模块文件和 ...

  6. Wall Treatment

    * wall treatment You can combine the turbulent flow interfaces with different types of wall treatmen ...

  7. 使用js获取页面的各种高度

    使用js获取相关高度: 获取网页被滚动条卷去的高度——兼容写法: scrollHeight = documen.body.scrollTop || document.documentElement.s ...

  8. LoadRunner中的参数与变量-产生20位的随机数

    LoadRunner中的参数与变量-产生20位的随机数 在LoadRunner脚本开发中,经常会遇到参数与变量相互转换的情况,本文对常见的转换情形进行了方法总结. 1.变量的赋值 //将字符串赋值给变 ...

  9. gitHub网站上常见英语翻译2

    repositories资料库 compilers with rich code analysis APIs.编译器具有丰富的代码分析API. plugins插件 With a variety of ...

  10. 【思维+贪心】codeforces Game of the Rows

    http://codeforces.com/contest/839/problem/B [题意] 给定n组人,告诉每组人的人数,这些人要在飞机上坐座位 飞机上座位的分布看图可以知道,12  3456 ...