1. 程式人生 > >TestNG資料驅動

TestNG資料驅動

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

TestNG資料驅動

testng的功能很強大,利用@DataProvider可以做資料驅動,資料來源檔案可以是EXCEL,XML,YAML,甚至可以是TXT文字。

@DataProvider註解簡介:

@DataProvider標記專門為測試方法提供引數的方法。這類方法必須返回Object[ ][ ]型別的二維陣列或者Iterator<Object>[],每一行Object[],都是測試方法的一個測試資料集,測試方法會為每個測試資料集執行一次。如果沒有指定引數的名稱,則預設為方法的名稱,方法的名稱沒有限制。

@DataProvider的小例子:

import java.lang.reflect.Method;

import org.testng.annotations.DataProvider;

import org.testng.annotations.Test;

publicclass test {

@DataProvider(name = "user")

public Object[][] createUser(Method m) {

        System.out.println(m.getName());

returnnew Object[][] { { 

"root""root" }, { "test""root" }, { "test""test" } };

    }

@Test(dataProvider = "user")

publicvoid verifyUser(String username, String password) {

        System.out.println("Verify User : " + username + ":" + password);

assertusername.equals(password);

    }

}

如上所示@DataProvider註解了createUser

方法,返回的二位數組裡有三行資料,每行兩列。所以@Test(dataProvider = "user")註解的verifyUser方法有兩個引數,用來接收每一行的兩個資料,如果createUser返回的資料陣列的列數和verifyUser的引數個數不同就會報錯的。因為返回的有三行,所以verifyUser會被執行三次。結果如下:

PASSED: verifyUser("root", "root")

FAILED: verifyUser("test", "root")

PASSED: verifyUser("test", "test")

CSV檔案資料讀取和@DataProvider

我自己做了一個以csv為例的測試架子,部分程式碼可通用。

CSV檔案讀取類(可通用,目錄自己可以修改,也可改變成讀取EXCEL、TXT等檔案):

import java.io.BufferedReader;

import java.io.File;

import java.io.FileReader;

import java.util.ArrayList;

import java.util.Iterator;

import java.util.List;

import java.util.Map;

import java.util.TreeMap;

import java.util.regex.Matcher;

publicclass CSVData implements Iterator<Object[]> {

private BufferedReader br        = null;

//行數

privateintrowNum    = 0;

//獲取次數

privateintcurRowNo  = 0;

//列數

privateintcolumnNum = 0;

//key

private String[]       columnName;

//csv中所有行資料

private List<String>   csvList;

//實際想要的行資料

private List<String>   csvListNeed;

/*

    * TestNG中由@DataProvider(dataProvider = "name")修飾的方法

    * csv時,呼叫此類構造方法(此方法會得到列名並將當前行移到下以後)執行後,轉發哦

    * TestNG自己的方法中去,然後由它們呼叫此類實現的hasNext()next()方法

    * 得到一行資料,然後返回給由@Test(dataProvider = "name")修飾的方法,如此

    * 反覆到資料讀完為止

    * 

    * 

    * @param filepath CSV檔名

    * @param casename 用例名

    */

public CSVData(String fileName, String caseId) {

try {

            File directory = new File(".");

            String ss = "resources.";

            File csv = new File(directory.getCanonicalFile() + "\\src\\test\\" + ss.replaceAll("\\.", Matcher.quoteReplacement("\\"))

                                + fileName + ".csv");

br = new BufferedReader(new FileReader(csv));

csvList = new ArrayList<String>();

while (br.ready()) {

csvList.add(br.readLine());

this.rowNum++;

            }

            String stringValue[] = csvList.get(0).split(",");

this.columnNum = stringValue.length;

columnName = new String[stringValue.length];

for (inti = 0; i < stringValue.lengthi++) {

columnName[i] = stringValue[i].toString();

            }

this.curRowNo++;

csvListNeed = new ArrayList<String>();

for (inti = 1; i < rowNumi++) {

                String values[] = csvList.get(i).split(",");

if (caseId.equals(values[0])) {

csvListNeed.add(csvList.get(i));

                }

            }

this.rowNum = 2;//就取一行

        } catch (Exception e) {

e.printStackTrace();

        }

    }

@Override

publicboolean hasNext() {

if (this.rowNum == 0 || this.curRowNo >= this.rowNum) {

try {

br.close();

            } catch (Exception e) {

e.printStackTrace();

            }

returnfalse;

        } else {

returntrue;

        }

    }

@Override

public Object[] next() {

/*

        * 將資料放入map 

        */

        Map<String, String> s = new TreeMap<String, String>();

        String csvCell[] = csvListNeed.get(0).split(",");

for (inti = 0; i < this.columnNumi++) {

            String temp = "";

try {

temp = csvCell[i].toString();

            } catch (ArrayIndexOutOfBoundsException ex) {

temp = "";

            }

s.put(this.columnName[i], temp);

        }

        Object r[] = new Object[1];

r[0] = s;

this.curRowNo++;

returnr;

    }

@Override

publicvoid remove() {

thrownew UnsupportedOperationException("remove unsupported");

    }

}

這個類實現了Iterator<Object[]>迭代器,TestNG呼叫此類實現的hasNext()next()方法得到一行資料,在next()方法中可以看到,我把資料是放在Map<String, String>中的,再把map放在Object[]裡,所以測試方法的引數就必須是一個Map<String, String>。我這裡改成了只讀取一行,因為一個csv檔案的一個caseId只應該有一行。

資料驅動類:

import java.lang.reflect.Method;

import java.util.Iterator;

import org.testng.annotations.DataProvider;

publicclass DataProviderTest {

/**

     * @DataProvider的返回值型別只能是Object[][]Iterator<Object>[]

     * 

     * @param method

     * @return

     */

@DataProvider

public Iterator<Object[]> dataSource(Method method) {

        return (Iterator<Object[]>) new CSVData(method.getDeclaringClass().getSimpleName(), method.getName());

    }

}

Method方法是通過反射獲取的,總之哪個方法呼叫我Method就是那個方法。

method.getDeclaringClass().getSimpleName()可以獲取方法所屬的類的類名。

我這裡規定了csv的檔名就是測試類的類名,用例名就是方法名。

return (Iterator<Object[]>) new CSVData()就是將CSV讀取類讀取的結果返回,返回的型別是Iterator<Object[]>的,符合@DataProvider的返回值型別要求。當@Test(dataProvider = "dataSource")註解的測試方法執行時就會呼叫IteratorhasNext()判斷是否有資料和next()獲取資料。

測試類:

import java.util.Map;

import org.testng.annotations.Test;

publicclass DataTest extends DataProviderTest {

@Test(dataProvider = "dataSource")

publicvoid id2(Map<String, String> data) {

        System.out.println(data);

    }

@Test(dataProvider = "dataSource")

publicvoid id1(Map<String, String> data) {

        System.out.println(data);

    }

}

DataTest.csv檔案如下:

輸出結果如下:

PASSED: id1({caseId=id1, flag=Y, property=flowModel, type=com.mybank.bkloanapply.core.model.BaseModel, [email protected]})

PASSED: id2({caseId=id2, flag=M, property=context, type=java.util.Map, value=a:[email protected]})

總結

通過以上例子可以看到,無論@DataProvider註解的方法返回的是Object[ ][ ]還是Iterator<Object>[],最後測試方法獲得的引數都是Object[ ]裡放的東西,第一個例子裡放了兩列String,第二個例子裡放了Map<String, String>,所以第一個測試類的測試方法的引數是兩個String,第二個測試類的測試方法的引數是Map<String, String>,必須保持一致才行。

這裡寫圖片描述