1. 程式人生 > >簡單聊聊TestNG中的併發

簡單聊聊TestNG中的併發

前言

最近在做專案裡的自動化測試工作,使用的是TestNG測試框架,主要涉及的測試型別有介面測試以及基於業務實際場景的場景化測試。由於涉及的場景大多都是大資料的作業開發及執行(如MapReduce、Spark、Hql等任務的執行),而這些任務的執行都需要耗費較多的時間。舉一個普遍的例子,其中一條場景測試用例是:

  • 執行一個MapReduce作業,校驗作業的執行結果和執行日誌。

對於一個最簡單的MR任務,如果YARN叢集資源充足,它的執行時間也要花上將近一分鐘的時間。更不用說當YARN叢集計算資源飽和時,任務還需要持續等待資源分配等。當測試迴歸用例集裡包含了大量此類的用例時,如果還用傳統的單執行緒執行方式,則一次自動化迴歸將會耗費大量的時間。

多執行緒並行執行

基於上述場景,我們可以考慮將自動化用例中相互之間沒有耦合關係,相對獨立的用例進行並行執行。如,我可以通過起不同的執行緒同時去執行不同的MR任務、Spark任務,每個執行緒各自負責跟蹤任務的執行情況。

此外,即使是單純的介面自動化測試,如果測試集裡包含了大量的用例時,我們也可以藉助於TestNG的多執行緒方式提高執行速度。

必須要指出的是,通過多執行緒執行用例時雖然可以大大提升用例的執行效率,但是我們在設計用例時也要考慮到這些用例是否適合併發執行,以及要注意多執行緒方式的通病:執行緒安全與共享變數的問題。建議是在測試程式碼中,儘可能地避免使用共享變數。如果真的用到了,要慎用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

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併發

前言 最近在做專案裡的自動化測試工作,使用的是TestNG測試框架,主要涉及的測試型別有介面測試以及基於業務實際場景的場景化測試。由於涉及的場景大多都是大資料的作業開發及執行(如MapReduce、Spark、Hql等任務的執行),而這些任務的執行都需要耗費較多的時間。舉一個普遍的例子,其中一條場景測試用例是

Java併發(10)- 簡單聊聊JDK的七大阻塞佇列

引言 JDK中除了上文提到的各種併發容器,還提供了豐富的阻塞佇列。阻塞佇列統一實現了BlockingQueue介面,BlockingQueue介面在java.util包Queue介面的基礎上提供了put(e)以及take()兩個阻塞方法。他的主要使用場景就是多執行緒下的生產者消費者模式,生產者執行緒通過put

Java併發--- 簡單聊聊JDK的七大阻塞佇列

JDK中除了上文提到的各種併發容器,還提供了豐富的阻塞佇列。阻塞佇列統一實現了BlockingQueue介面,BlockingQueue介面在java.util包Queue介面的基礎上提供了put(e)以及take()兩個阻塞方法。他的主要使用場景就是多執行緒下

Java並發(10)- 簡單聊聊JDK的七大阻塞隊列

所有 poll 嘗試 sig blocking 模式 繼續 queue 都是 引言 JDK中除了上文提到的各種並發容器,還提供了豐富的阻塞隊列。阻塞隊列統一實現了BlockingQueue接口,BlockingQueue接口在java.util包Queue接口的基礎上

聊聊Java併發佇列 有界佇列和無界佇列的區別

本文主要總體的說一說各種併發佇列  首先來一張全體照  從有界無界上分  常見的有界佇列為 ArrayBlockingQueue 基於陣列實現的阻塞佇列 LinkedBlockingQueue 其實也是有界佇列,但是不設定大小時就時Integer.MAX_VALUE

簡單理解javascript的原型對象,實現對之間共享屬性和行為

type屬性 定義 say 能夠 方法 () post spa popu javascript中提供了構造函數。可以方便的創建對象。典型的構造函數例如以下: function Person(name, age) {   this.name = name;

簡單談談js的MVC

包含 mod .cn 所有 代碼 head 性能 行高 模式 MVC是什麽? MVC是一種架構模式,它將應用抽象為3個部分:模型(數據)、視圖、控制器(分發器)。 本文將用一個經典的例子todoList來展開(代碼在最後)。 一個事件發生的過程(通信單向流動): 1、用

簡單介紹 C++變量的引用

簡單 變量 9.png -1 技術分享 png log 介紹 .com 簡單介紹 C++中變量的引用

eclipse簡單配置 eclipse使用github Eclipse 快捷鍵

space f11 鼠標 edi ctrl master 提交 col note 將eclipse壓縮包解壓到英文目錄下 eclipse簡單配置 設置字符集 Window——Preferences——General——Workspace——Text file Enco

從2個命令簡單聊聊CentOS賬戶鎖定原理

passwd usermod centos賬戶鎖定實驗環境#uname -aLinux a69.hunk.edu 2.6.32-696.el6.x86_64 #1 SMP Tue Mar 21 19:29:05 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux#cat /etc/

簡單聊聊SOA和微服務

利用 java res content 視野 分布式數據庫 而不是 frame 分庫分表 轉自:https://juejin.im/post/592f87feb123db0064e5ef7c (2017-06) 簡單聊聊SOA和微服務 架構設計中的樸素主義

Java簡單模擬AndroidHandler-Message機制

就是 示例代碼 pri 技術分享 android ble [] handle @override 在Android中主線程與子線程的通信十分重要,Google工程師為我們提供了Handler-Message機制來解決他們之間的交互問題。今天,我們就來簡單理解Handler-

搭建簡單的LAMP出現的小問題

apache mysql php 當運行程序的時候發現出現這樣的字符:httpd: Could not reliably determine the server's fully qualified domain name, using www.shifanyong.cn for S

聊聊Spring的工廠

通過 特性 能力 都是 eat tlist app prototype 實例化 BeanFactory是Spring IOC容器的根接口,定義了Bean工廠的最基礎的功能特性,比如根據name獲取指定bean等,根據不同用途它的子接口又對它的功能進行細化,比如是否是可列表的

簡單說說Javascript的作用域鏈

ole strong text TE 結果 span 全局對象 範圍 undefine Javascript中作用域就是變量與函數的可訪問範圍,即作用域控制著變量與函數的可見性和生命周期。變量的作用域有全局作用域和局部作用域兩種。當查找變量的時候,會先從當前上下文的

簡單談談MySQL的int(m)

探討 class clear duplicate 創建 int 一個數 value tar 轉載地址:https://www.jb51.net/article/93760.htm 設置int型的時候,需要設置int(M),以前知道這個M最大是255,但是到底應該設置多少並

簡單介紹linux的shell腳本

-a sun 程序設計 set 每次 image line ans 交互 一、shell基本介紹 Shell就是一個命令行解釋器,它的作用是解釋執行用戶的命令,用戶輸入一條命令,Shell就解釋執行一條,這種方式稱為交互式。 Shell還有一種執行命令的方式稱為

testNGdataprovider使用的兩種方式

pro ret urn expect highlight 參數化 sdn esp tin testNG的參數化測試有兩種方式:xml和dataprovider.個人更喜歡dataprovider,因為我喜歡把測試數據放在數據庫裏。 一.返回類型是Iterator<Ob

簡單說說Kafka的時間輪算法

而且 超過 lsi alt UNC -c wheel lean num 零、時間輪定義 簡單說說時間輪吧,它是一個高效的延時隊列,或者說定時器。實際上現在網上對於時間輪算法的解釋很多,定義也很全,這裏引用一下朱小廝博客裏出現的定義: 參考下圖,Kafka中的時間輪(Timi

原 薦 簡單說說Kafka的時間輪算法

nds type 說明 同學 秒級 高層 那是 忽略 tick 零、時間輪定義 簡單說說時間輪吧,它是一個高效的延時隊列,或者說定時器。實際上現在網上對於時間輪算法的解釋很多,定義也很全,這裏引用一下 朱小廝博客 裏出現的定義: 參考下圖,Kafka中的時間輪(Timi