天天看點

走進Java接口測試之測試架構TestNG資料驅動(入門篇)

我們在前面的文章中,和大家分享過接口自動化測試一些基本的實作方法,但是,你很快就會發現,如果在測試腳本中寫死測試資料的話,測試腳本靈活性會非常低。而且,對于那些具有重複的請求,而隻是測試入參不同的用例來說,就會存在大量重複的代碼。那麼怎麼把自己從簡單、重複的工作中解放出來呢?這個時候我們應考慮把測試資料和測試腳本分離,也就是說資料驅動。

資料驅動很好地解決了大量重複腳本的問題,實作了“測試腳本和資料的解耦”。目前幾乎所有主流的自動化測試工具和架構都支援。

資料驅動測試的資料不僅可以包括測試輸入資料,還可以包含測試驗證結果資料,甚至可以包含測試邏輯分支的控制變量。

資料驅動的測試思想不僅适用于接口測試,也适合與單元測試,UI自動化測試,性能測試等

寫死

txt檔案

Json

Yaml

配置檔案properties

execl

db

網絡中

測試腳本中通過 <code>data provider</code> 去資料源中讀取一行資料,指派給相應的變量,執行用例。接着再去檔案中讀取下一行資料,讀取完所有的資料後,測試結束。參數化檔案中有幾行資料,測試用例就會被執行幾次。如圖所示:

走進Java接口測試之測試架構TestNG資料驅動(入門篇)

我們可以在每個測試方法上使用任意數量的參數,并訓示 TestNG 使用 <code>@Parameters</code> 注釋傳遞正确的參數。

TestNG有兩種方法可以設定這些參數(@Factory 資料工廠不在此介紹):

使用 testng.xml 

走進Java接口測試之測試架構TestNG資料驅動(入門篇)

DataProvider 

走進Java接口測試之測試架構TestNG資料驅動(入門篇)
走進Java接口測試之測試架構TestNG資料驅動(入門篇)

 注意:

TestNG.xml 中的參數可以是套件或測試級别;

DataProvider 中的參數可以将 Method 和 ITestContext 作為參數。

如果簡單參數,則可以在 testng.xml 中指定它們,在以下代碼中,我們指定的參數 name 和 age 值。此 XML 參數在 testng.xml 中 定義:

<code>&lt;suite name="parameter"&gt;</code>

<code>&lt;test name="param"&gt;</code>

<code>&lt;parameter name="name" value="zhangsan"/&gt;</code>

<code>&lt;parameter name="age" value="10"/&gt;</code>

<code>&lt;classes&gt;</code>

<code>&lt;class name="com.zuozewei.springboottestngdatadrivendemo.paramter.ParamterTest"/&gt;</code>

<code>&lt;/classes&gt;</code>

<code>&lt;/test&gt;</code>

<code>&lt;/suite&gt;</code>

測試方法将分别接收參數 name 和 age 的值。

<code>@Slf4j</code>

<code>public class ParamterTest {</code>

<code>@Test</code>

<code>@Parameters({"name","age"})</code>

<code>public void paramTest(String name,int age){</code>

<code>log.info("name = [{}] ; age = [{}]" ,name,age);</code>

<code>}</code>

注意 @Parameters 可以被放置在下列位置:

在任何已經有 @Test,@Before/After 或 @Factory 注釋的方法上;

最多隻有一個測試類的構造函數。在這種情況下,TestNG 将調用此特定構造函數,并在需要執行個體化測試類時将參數初始化為 testng.xml 中指定的值。此功能可用于将類中的字段初始化為測試方法随後将使用的值。

<code>@Parameters({ "name", "age" })</code>

<code>@BeforeMethod</code>

<code>public void beforeTest(String name, String age) {</code>

<code>m_name = name; // 查詢資料源值</code>

<code>m_age = age;</code>

注意:

XML 參數按照與注釋中相同的順序映射到 Java 參數,如果數字不比對,TestNG 将報錯;

參數是存在作用域的。在 testng.xml 中,可以在 suite 标記下或 test 下聲明它們 。如果兩個參數具有相同的名稱,則它是 test 中定義的具有優先權。如果需要指定适用于所有測試的參數并僅為某些測試覆寫其值,這将非常友善。

如果需要傳遞複雜參數或需要從 Java 建立的參數(複雜對象,從檔案或資料庫讀取的對象等等),則在 testng.xml 中指定參數可能不夠。在這種情況下,可以使用資料提供程式提供測試所需的值。資料提供程式是類上的一個方法,它傳回一組對象數組。此方法使用 @DataProvider 注釋。

@DataProvider函數,需要定義屬性 name:

<code>@DataProvider(name="data")</code>

<code>public Object[][] providerData(){</code>

<code>Object[][] objects = new Object[][]{</code>

<code>{"zhangsan",10},</code>

<code>{"lisi",20},</code>

<code>{"wangwu",30}</code>

<code>};</code>

<code>return objects;</code>

@Test 測試用例,屬性 dataProvider 需要指定對應的資料提供者名稱。

<code>@Test(dataProvider = "data")</code>

<code>public void testDataProvider(String name,int age){</code>

執行結果:

<code>name = [zhangsan] ; age = [10]</code>

<code>name = [lisi] ; age = [20]</code>

<code>name = [wangwu] ; age = [30]</code>

<code>===============================================</code>

<code>Default Suite</code>

<code>Total tests run: 3, Failures: 0, Skips: 0</code>

@DataProvider 函數可以插入 Method 和 ITestContext 類型參數,這兩個參數裡面可以擷取很多有用的資訊。

@DataProvider函數:

<code>@DataProvider(name="methodData")</code>

<code>public Object[][] methodDataTest(Method method){</code>

<code>Object[][] result=null;</code>

<code>if(method.getName().equals("test1")){</code>

<code>result = new Object[][]{</code>

<code>{"zhangsan",20},</code>

<code>{"lisi",25}</code>

<code>}else if(method.getName().equals("test2")){</code>

<code>{"wangwu",50},</code>

<code>{"zhaoliu",60}</code>

<code>return result;</code>

@Test 測試執行腳本:

<code>@Test(dataProvider = "methodData")</code>

<code>public void test1(String name,int age){</code>

<code>log.info("test111方法: name = [{}] ; age = [{}]" ,name,age);</code>

<code>public void test2(String name,int age){</code>

<code>log.info("test222方法: name = [{}] ; age = [{}]" ,name,age);</code>

<code>test111方法: name = [zhangsan] ; age = [20]</code>

<code>test111方法: name = [lisi] ; age = [25]</code>

<code>test222方法: name = [wangwu] ; age = [50]</code>

<code>test222方法: name = [zhaoliu] ; age = [60]</code>

<code>Total tests run: 7, Failures: 0, Skips: 0</code>

有的場景我們需要大量參數進行讀取,比如參數資料源是 DB,而資料達到百萬級,這樣測試程式周遊所有資料時,可能就會導緻記憶體溢出,

那麼我們怎樣解決這個問題?當我們擷取了一條資料,對它執行測試方法,然後就廢棄這個資料對象,再測試下一個書。這個原則是延遲初始化,這個思想就是當你真正需要一個對象時才建立它,而不是提前建立它。

為了實作這種方法,TestNG 允許我們從資料提供者傳回一個 Iterator 對象,而不是一個二維對象數組。

Iterator 是 java.util 包中的一個接口,它的方法簽名如下:

<code>public interface Iterator&lt;E&gt; {</code>

<code>boolean hasNext();</code>

<code>E next();</code>

<code>default void remove();</code>

它可以通過 next 調用下一組資料,這樣就有機會在最後一刻執行個體化相應的對象,即剛好在需要在這些參數的測試方法被調用之前。

下面例子是重寫後的例子,我們實作了一個 Iterator,它将傳回 4 個帶有不同ID的對象:

<code>public class AccoutIterator implements Iterator {</code>

<code>private int index =0;</code>

<code>static private final int MAX =4;</code>

<code>@Override</code>

<code>public boolean hasNext() {</code>

<code>return index &lt; MAX;</code>

<code>public Object next() {</code>

<code>return new Object[]{</code>

<code>//這裡就是放入要實作的對象或者一組資料</code>

<code>"延遲資料提供:"+ (index++)</code>

<code>public void remove() {</code>

<code>throw new UnsupportedOperationException("remove");</code>

@DataProvider函數調用:

<code>@DataProvider(name = "iterator")</code>

<code>public Iterator&lt;Object[]&gt; iteratorDataProvider(){</code>

<code>return new AccoutIterator();</code>

@Test測試運作函數:

<code>@Test(dataProvider = "iterator")</code>

<code>public void testcase2(String name){</code>

<code>log.info(" name = [{}] " ,name);</code>

運作結果:

<code>name = [延遲資料提供:0]</code>

<code>name = [延遲資料提供:1]</code>

<code>name = [延遲資料提供:2]</code>

<code>name = [延遲資料提供:3]</code>

<code>Total tests run: 4, Failures: 0, Skips: 0</code>

資料提供程式可以與并行屬性并行運作:

<code>@DataProvider(parallel = true)</code>

<code>// ...</code>

從 XML 檔案運作的并行資料提供程式共享相同的線程池,預設情況下大小為 10。可以在 XML 檔案的 suite 标記中修改此值:

<code>&lt;suite name="Suite1" data-provider-thread-count="20" &gt;</code>

如果要在不同的線程池中運作幾個特定的資料提供程式,則需要從其他 XML檔案運作它們。

這篇的知識點:

需要參數化來建立資料驅動測試;

TestNG 支援兩種參數化,使用 @Parameter + TestNG.xml 并使用 @DataProvider;

在 @Parameter + TestNG.xml中,參數可以放在套件級别和測試級别。如果在兩個地方聲明相同的參數名稱,測試級别參數将優先于套裝級别參數;

使用 @Parameter + TestNG.xml,一次隻能設定一個值,但 @DataProvider 傳回一個2維的 Object 數組;

如果 DataProvider 存在于不同的類中,那麼測試方法所在的類,DataProvider 應該是靜态方法;

有通過支援兩個參數的 DataProvider 的方法和 ITestContext;

TestNG 允許我們從資料提供者傳回一個 Iterator 對象,實作延遲提供資料。

當然,DataProvider 隻是從行為操作上分離了資料的提供方式,沒有從根本上解決自動化測試中測試資料本身的穩定性、快速響應變化、資料丢失、資料被修改的這些難點和阻礙:

比如生産資料庫裡的資料導入并重新整理測試資料庫,之前用例裡使用的資料被覆寫;

比如幾個小組在一個系統裡使用同一個測試資料庫,AB組使用存在交叉,B組還要把資料改變一下再用,或者B組用完後測試資料已經發生改變;

比如使用的測試資料具備時效性,狀态會改變的,從 active 變成 inactive 的等;

自動化測試的其他方面都不是什麼大問題,最主要的阻礙就是測試資料本身(特别是在真實的測試環境上時)。

本文源碼:

https://github.com/7DGroup/Java-API-Test-Examples/tree/master/springboot-testng-data-driven-demo