我們在前面的文章中,和大家分享過接口自動化測試一些基本的實作方法,但是,你很快就會發現,如果在測試腳本中寫死測試資料的話,測試腳本靈活性會非常低。而且,對于那些具有重複的請求,而隻是測試入參不同的用例來說,就會存在大量重複的代碼。那麼怎麼把自己從簡單、重複的工作中解放出來呢?這個時候我們應考慮把測試資料和測試腳本分離,也就是說資料驅動。
資料驅動很好地解決了大量重複腳本的問題,實作了“測試腳本和資料的解耦”。目前幾乎所有主流的自動化測試工具和架構都支援。
資料驅動測試的資料不僅可以包括測試輸入資料,還可以包含測試驗證結果資料,甚至可以包含測試邏輯分支的控制變量。
資料驅動的測試思想不僅适用于接口測試,也适合與單元測試,UI自動化測試,性能測試等
寫死
txt檔案
Json
Yaml
配置檔案properties
execl
db
網絡中
測試腳本中通過 <code>data provider</code> 去資料源中讀取一行資料,指派給相應的變量,執行用例。接着再去檔案中讀取下一行資料,讀取完所有的資料後,測試結束。參數化檔案中有幾行資料,測試用例就會被執行幾次。如圖所示:

我們可以在每個測試方法上使用任意數量的參數,并訓示 TestNG 使用 <code>@Parameters</code> 注釋傳遞正确的參數。
TestNG有兩種方法可以設定這些參數(@Factory 資料工廠不在此介紹):
使用 testng.xml
DataProvider
注意:
TestNG.xml 中的參數可以是套件或測試級别;
DataProvider 中的參數可以将 Method 和 ITestContext 作為參數。
如果簡單參數,則可以在 testng.xml 中指定它們,在以下代碼中,我們指定的參數 name 和 age 值。此 XML 參數在 testng.xml 中 定義:
<code><suite name="parameter"></code>
<code><test name="param"></code>
<code><parameter name="name" value="zhangsan"/></code>
<code><parameter name="age" value="10"/></code>
<code><classes></code>
<code><class name="com.zuozewei.springboottestngdatadrivendemo.paramter.ParamterTest"/></code>
<code></classes></code>
<code></test></code>
<code></suite></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<E> {</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 < 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<Object[]> 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><suite name="Suite1" data-provider-thread-count="20" ></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