走進Java接口測試之測試框架TestNG數據驅動(入門篇)

前言

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

數據驅動的優勢?

  • 數據驅動很好地解決了大量重複腳本的問題,實現了“測試腳本和數據的解耦”。目前幾乎所有主流的自動化測試工具和框架都支持。
  • 數據驅動測試的數據不僅可以包括測試輸入數據,還可以包含測試驗證結果數據,甚至可以包含測試邏輯分支的控制變量。
  • 數據驅動的測試思想不僅適用於接口測試,也適合與單元測試,UI自動化測試,性能測試等

常見提供數據的方式?

  • 硬編碼
  • txt文件
  • Json
  • Yaml
  • 配置文件properties
  • excel
  • db
  • 網絡中

數據驅動的原理?

測試腳本中通過 data provider 去數據源中讀取一行數據,賦值給相應的變量,執行用例。接着再去文件中讀取下一行數據,讀取完所有的數據後,測試結束。參數化文件中有幾行數據,測試用例就會被執行幾次。如圖所示:
在這裏插入圖片描述

TestNG如何實現?

我們可以在每個測試方法上使用任意數量的參數,並指示 TestNG 使用 @Parameters 註釋傳遞正確的參數。

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

  • 使用 testng.xml
    在這裏插入圖片描述
  • DataProvider
    在這裏插入圖片描述

在這裏插入圖片描述
注意:

  • TestNG.xml 中的參數可以是套件或測試級別;
  • DataProvider 中的參數可以將 Method 和 ITestContext 作爲參數。

testng.xml 中的參數

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

<suite name="parameter">
    <test name="param">
        <parameter name="name" value="zhangsan"/>
        <parameter name="age" value="10"/>

        <classes>
            <class name="com.zuozewei.springboottestngdatadrivendemo.paramter.ParamterTest"/>
        </classes>
    </test>
</suite>

測試方法將分別接收參數 name 和 age 的值。

@Slf4j
public class ParamterTest {

    @Test
    @Parameters({"name","age"})
    public void paramTest(String name,int age){
        log.info("name = [{}] ; age = [{}]" ,name,age);
    }

}

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

  • 在任何已經有 @Test,@Before/After 或 @Factory 註釋的方法上;
  • 最多隻有一個測試類的構造函數。在這種情況下,TestNG 將調用此特定構造函數,並在需要實例化測試類時將參數初始化爲 testng.xml 中指定的值。此功能可用於將類中的字段初始化爲測試方法隨後將使用的值。
@Parameters({ "name", "age" })
@BeforeMethod
public void beforeTest(String name, String age) {
	m_name = name;                              // 查詢數據源值
	m_age = age;
}

注意:

  • XML 參數按照與註釋中相同的順序映射到 Java 參數,如果數字不匹配,TestNG 將報錯;
  • 參數是存在作用域的。在 testng.xml 中,可以在 suite 標記下或 test 下聲明它們 。如果兩個參數具有相同的名稱,則它是 test 中定義的具有優先權。如果需要指定適用於所有測試的參數並僅爲某些測試覆蓋其值,這將非常方便。

使用 DataProviders 的參數

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

簡單使用

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

	@DataProvider(name="data")
 	public Object[][] providerData(){
        Object[][] objects = new Object[][]{
                {"zhangsan",10},
                {"lisi",20},
                {"wangwu",30}
        };

        return objects;
    }

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

    @Test(dataProvider = "data")
    public void testDataProvider(String name,int age){
        log.info("name = [{}] ; age = [{}]" ,name,age);
    }

執行結果:

name = [zhangsan] ; age = [10]
name = [lisi] ; age = [20]
name = [wangwu] ; age = [30]

===============================================
Default Suite
Total tests run: 3, Failures: 0, Skips: 0
===============================================

@DataProvider函數插入參數使用

@DataProvider 函數可以插入 Method 和 ITestContext 類型參數,這兩個參數裏面可以獲取很多有用的信息。

@DataProvider函數:

  @DataProvider(name="methodData")
    public Object[][] methodDataTest(Method method){
        Object[][] result=null;

        if(method.getName().equals("test1")){
            result = new Object[][]{
                    {"zhangsan",20},
                    {"lisi",25}
            };
        }else if(method.getName().equals("test2")){
            result = new Object[][]{
                    {"wangwu",50},
                    {"zhaoliu",60}
            };
        }

        return result;
    }

@Test 測試執行腳本:

  @Test(dataProvider = "methodData")
    public void test1(String name,int age){
        log.info("test111方法: name = [{}] ; age = [{}]" ,name,age);
    }

    @Test(dataProvider = "methodData")
    public void test2(String name,int age){
        log.info("test222方法: name = [{}] ; age = [{}]" ,name,age);
    }

執行結果:

test111方法: name = [zhangsan] ; age = [20]
test111方法: name = [lisi] ; age = [25]
test222方法: name = [wangwu] ; age = [50]
test222方法: name = [zhaoliu] ; age = [60]

===============================================
Default Suite
Total tests run: 7, Failures: 0, Skips: 0
===============================================

延遲數據提供者

有的場景我們需要大量參數進行讀取,比如參數數據源是 DB,而數據達到百萬級,這樣測試程序遍歷所有數據時,可能就會導致內存溢出。

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

爲了實現這種方法,TestNG 允許我們從數據提供者返回一個 Iterator 對象,而不是一個二維對象數組。

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

public interface Iterator<E> {

    boolean hasNext();
    E next();
    default void remove();

 }

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

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

public class AccoutIterator implements Iterator {

    private int index =0;
    static private final int MAX  =4;

    @Override
    public boolean hasNext() {
        return index < MAX;
    }

    @Override
    public Object next() {
        return new Object[]{
                //這裏就是放入要實現的對象或者一組數據
                "延遲數據提供:"+ (index++)
        };
    }

    @Override
    public void remove() {
        throw new UnsupportedOperationException("remove");
    }

@DataProvider函數調用:

 @DataProvider(name = "iterator")
    public Iterator<Object[]> iteratorDataProvider(){
        return new AccoutIterator();

    }

@Test測試運行函數:

 @Test(dataProvider = "iterator")
    public void testcase2(String name){
       log.info(" name = [{}] " ,name);
    }

運行結果:

name = [延遲數據提供:0] 
name = [延遲數據提供:1] 
name = [延遲數據提供:2] 
name = [延遲數據提供:3] 

===============================================
Default Suite
Total tests run: 4, Failures: 0, Skips: 0
===============================================

其他的高級玩法

數據提供程序可以與並行屬性並行運行:

@DataProvider(parallel = true)
// ...

從 XML 文件運行的並行數據提供程序共享相同的線程池,默認情況下大小爲 10。可以在 XML 文件的 suite 標記中修改此值:

<suite name="Suite1" data-provider-thread-count="20" >

如果要在不同的線程池中運行幾個特定的數據提供程序,則需要從其他 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/zuozewei/Java-API-Test-Examples/tree/master/springboot-testng-data-driven-demo

參考資料:
[1]:https://blog.csdn.net/LangSand/article/details/53895654
[2]:https://testng.org/doc/index.html
[3]:軟件測試52講 茹炳晟

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章