1. 程式人生 > >excel資料驅動介面測試框架

excel資料驅動介面測試框架

                                      介面測試自動化框架搭建

                                                                                                                     作者:楊剛

前言:

經常,在應用程式的業務邏輯中存在大量的這樣的介面:他們接受不同的輸入,然後進行或驗證,或處理,進而完成相同的流程。比如網站的登入入口,使用者名稱和密碼都有長度的限制,同時也具有是否允許特殊字元的限制等,所以在我們進行其單元測試的過程中,根據不同長度的使用者名稱和密碼,以及不同的字元組合,只需要提供相同的測試程式碼結構,就能完成測試,不同的僅僅測試資料與期望值,但是因為每一個測試方法中的輸入引數不同,我們必須為每一個輸入組編寫單獨的測試用例,從而產生大量冗餘程式碼,十分不便於維護。

幸好,本文所述的 Feed4JUnit 良好的解決了資料與程式碼分離的問題,Feed4JUnit JUnit測試框架的擴充套件,它通過操作來自於檔案以及不同的資料來源的測試資料,使您的單元測試變得更容易編寫與維護。

使用工具

Eclipse+Java+httpclient+feed4junit+Junit4

二、Feed4JUnit 的下載及安裝

1. Feed4JUnit 是開源的測試元件,您可以從如下連結下載最新版本:

2. 解壓下載的 zip包,複製整個 lib資料夾到您的 Java專案的根目錄,如圖 1

圖 1. 複製 lib 到專案根目錄

3. 選定專案,右鍵選擇專案的屬性,然後通過

Add JARs將步驟 2 lib 資料夾下的所有 Jar新增到專案的 BuildPath下,如圖2

圖 2. 新增 Jar 到 Build Path

三、使用 Feed4JUnit 實現資料與程式碼分離的測試

Feed4JUnit 的資料來源可以包括以下幾種型別-檔案 (CSV或者Excel )、資料庫、自定義資料來源。

Feed4JUnit使用一個特殊的執行類Feeder.class,用來支援與標識引數化測試,如果您想要編寫資料與程式碼分離的測試指令碼,必須在您的測試類上增加註釋@RunWith(Feeder.class)。同時,您需要使用@Test來標示您實現測試的方法,並且使用

@Source來宣告和接收資料來源的資料,基本的程式碼結構如清單3所示:

清單 3. 測試程式碼結構

package Living;

import static org.junit.Assert.*;

import org.databene.benerator.anno.InvocationCount;

import org.databene.benerator.anno.Source;

import org.junit.After;

import org.junit.AfterClass;

import org.junit.Before;

import org.junit.BeforeClass;

import org.junit.Test;

import org.junit.runner.RunWith;

import Pub.LivingPub;

import org.databene.feed4junit.Feeder;

@RunWith(Feeder.class)

public class PushTest{

    @BeforeClass

    public static void setUpBeforeClass()throws Exception {

    }

    @AfterClass

    public static void tearDownAfterClass()throws Exception {

    }

    @Before

    public void setUp() throws Exception {

    }

    @After

    public void tearDown() throws Exception {

    }

    @Test

//  @InvocationCount(1) //指定測試的次數

    @Source("D:/data/Living/push.xlsx")//指定測試的資料來源

    public void pushTest(String cases,StringclassroomId,Stringname,

           String loginToken,StringquestionUrl,StringquestionId,Stringcorrect,

           String courseLevelId,Stringanswers,booleanexpected) {

        assertEquals(expected, LivingPub.push(cases,classroomId,name,loginToken,questionUrl,questionId,correct,courseLevelId,answers));

        }

}

以檔案作為資料來源

Feed4JUnit支援從 CSV或者 Excel檔案裡面讀取資料作為輸入,這裡我們以Excel檔案為例。

1. D:/data/Living/目錄下建立push.xlsx資料檔案,樣例資料如圖3,預設情況下,第一行會以列名存在,在執行過程中不會作為資料讀取。

圖 3. Excel 資料來源

2. 建立測試類並在接收資料的測試方法上宣告資料來源為@Source("D:/data/Living/push.xlsx")Excel中的資料在傳遞過程中會自動按照列與測試方法的引數的位置順序進行匹配,並以行作為一個單位讀取並傳遞給測試方法體。比如圖3中的 cases列的值會做為方法的第一個引數傳入方法體中,classroomId列的值會作為方法的第二個引數,以此類推。在測試進行過程中,首先在Excel檔案中讀取一行(包含三列),接著按照位置順序將資料傳遞到方法體中(每列按順序對應一個引數)進行執行,執行完成後讀取Excel中的下一行進行相同流程的測試,其原理與Java中的迭代器十分類似。請注意當資料檔案中資料的列數小於測試方法引數的個數的時候,測試會因為位置不匹配而失敗。

3. 執行測試,因為 Feed4Junit JUnit的擴充套件,所以執行方式與 JUnit完全相同,即以 JUnit執行即可,執行結果如圖 4所示,我們可以看到,Data.xls中的資料已全部傳入測試方法並執行。

圖 4. 執行結果示例

四、測試公共類及http模擬客戶端實現

介面請求公共類及http模擬客戶端:

1、 介面請求公共類程式碼:

public static boolean  push(String cases,String classroomId,String name,
String loginToken,String questionUrl,String questionId,String correct,
String courseLevelId,String answers) {
String url="xxxx";
Map<String, String> headers=new HashMap<String,String>();
Map<String, String> params=new HashMap<String,String>();
params.put("classroomId", classroomId);
params.put("name", name);
params.put("loginToken", loginToken);
JSONObject question=new JSONObject();
question.put("questionUrl", questionUrl);
question.put("questionId", questionId);
question.put("correct", correct);
question.put("courseLevelId", courseLevelId);
question.put("answers", answers);
params.put("question", question.toString());
String responseContent = PublicClient.getInstance().sendHttpGet(url, params,headers);
System.out.println(cases+"result:" + responseContent); 
JSONObject js=JSONObject.fromObject(responseContent);
String rscode=js.getString("code");
System.out.println(rscode);
//判斷返回值
if (rscode.equals("10001"))
return false;
if (rscode.equals("10101"))
return false;
if (rscode.equals("10002"))
return false;
if (rscode.equals("10103"))
return false;
if (rscode.equals("10102"))
return false;
if (rscode.equals("10104"))
return false;
if (rscode.equals("11001"))
return false;
if (rscode.equals("11002"))
return false;
if (rscode.equals("999999"))
return false;
return true;
}

2、 模擬客戶端程式碼:

public String sendHttpGet(String httpUrl,Map<String, String> params,Map<String, String> Headers) { 
//StringBuffer param = new StringBuffer();  
//        int i = 0;  
        if(params != null && !params.isEmpty()){
List<NameValuePair> pairs = new ArrayList<NameValuePair>(params.size());
for(Map.Entry<String,String> entry : params.entrySet()){
String value = entry.getValue();
if(value != null){
pairs.add(new BasicNameValuePair(entry.getKey(),value));
}else {
pairs.add(new BasicNameValuePair(entry.getKey(),""));
}
}
try {
httpUrl += "?" + EntityUtils.toString(new UrlEncodedFormEntity(pairs, "UTF-8"));
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
HttpGet httpGet = new HttpGet(httpUrl);
   if (Headers != null && !Headers.isEmpty()) {  
            for (Entry<String, String> entry : Headers.entrySet()) {  
            // 排除掉空值 
                if (entry.getValue() != null) {  
                httpGet.addHeader(entry.getKey(), entry.getValue()  
                            .toString());  
                } else {
                httpGet.setHeader(entry.getKey(), ""); 

            }  
        }
return sendHttpGet(httpGet);
}

/**
* 傳送Get請求
* @param httpGet
* @return
*/
private String sendHttpGet(HttpGet httpGet) {
CloseableHttpClient httpClient = null;
CloseableHttpResponse response = null;
HttpEntity entity = null;
String responseContent = null;
try {
// 建立預設的httpClient例項.
httpClient = HttpClients.createDefault();
httpGet.setConfig(requestConfig);
// 執行請求
response = httpClient.execute(httpGet);
entity = response.getEntity();
responseContent = EntityUtils.toString(entity, "UTF-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
// 關閉連線,釋放資源
if (response != null) {
response.close();
}
if (httpClient != null) {
httpClient.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return responseContent;
}