使用 Feed4JUnit 進行資料與程式碼分離的 Java 單元測試

不淨之心發表於2013-03-18
[url]http://www.ibm.com/developerworks/cn/java/j-lo-feed4junit/index.html[/url]
簡介: JUnit 是被廣泛應用的 Java 單元測試框架,但是它沒有很好的提供引數化測試的支援,很多測試人員不得不把測試資料寫在程式裡或者通過其它方法實現資料與程式碼的分離,在後續的修改和維護上有諸多限制和不便。Feed4JUnit 是開源的基於 JUnit 的擴充套件,通過使用 Feed4JUnit 提供的註釋,使用者可以很方便的把測試資料存放在檔案或其它資料來源。本文通過介紹及簡單示例,使讀者瞭解並能夠使用 Feed4JUnit, 方便的實現資料與程式碼分離的測試。

Feed4JUnit 與 JUnit
經常,在應用程式的業務邏輯中存在大量的這樣的介面:他們接受不同的輸入,然後進行或驗證,或處理,進而完成相同的流程。比如網站的登入入口,使用者名稱和密碼都有長度的限制,同時也具有是否允許特殊字元的限制等,所以在我們進行其單元測試的過程中,根據不同長度的使用者名稱和密碼,以及不同的字元組合,只需要提供相同的測試程式碼結構,就能完成測試,不同的僅僅測試資料與期望值,但是因為每一個測試方法中的輸入引數不同,我們必須為每一個輸入組編寫單獨的測試用例,從而產生大量冗餘程式碼,十分不便於維護。
基於以上場景,JUnit 4 提供了引數化的特性,從而能夠實現不同資料輸入對相同測試程式碼的測試,如清單 1 所示:
[img]http://www.ibm.com/developerworks/cn/java/j-lo-feed4junit/image003.jpg[/img]

清單 1. JUnit 4 引數化測試程式碼示例
[img]http://www.ibm.com/developerworks/cn/java/j-lo-feed4junit/image005.jpg[/img]

package sample.test;

import static org.junit.Assert.assertEquals;

import java.util.Arrays;
import java.util.Collection;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

import sample.code.UserAccess;

/*
* JUnit - Parameter test sample
*/
@RunWith(Parameterized.class)
public class JunitSample {

private String user;
private String pw;
private boolean expected;

@Parameters
public static Collection dataGenerater() {
return Arrays.asList(new Object[][] {
{ "user01", "123456", true },
{ "helloworld", "123456", false },
{ "david", "re*ads", false },
{ "goodone", "onegood", true } });
}

public JunitSample(String user, String pw, boolean expected) {
this.user = user;
this.pw = pw;
this.expected = expected;
}

@Test
public void testAccessCheck() {
assertEquals(expected, UserAccess.accessCheck(user, pw));
}
}



通過以上示例程式碼可以看出,JUnit 4 通過使用一個標記 @Parameters 註釋的返回型別為 Collection 的靜態方法產生資料,測試資料通過變數傳遞給測試方法,從而完成多資料輸入的測試。但是隨著業務的需要,測試人員需要經常增加測試資料與修改現有測試資料,JUnit 4 提供的硬編碼方式已經愈顯笨重和不便,資料與程式碼分離顯得尤為重要。
幸好,本文所述的 Feed4JUnit 良好的解決了資料與程式碼分離的問題,Feed4JUnit 是 JUnit 測試框架的擴充套件,它通過操作來自於檔案以及不同的資料來源的測試資料,使您的單元測試變得更容易編寫與維護。
本文將通過示例向您展示 Feed4JUnit 的安裝以及測試程式碼與資料分離的實現,請注意本文的示例程式碼全部基於針對如下一個十分簡單使用者登入檢驗的類,並且假定您正在使用 Eclipse 作為您的 IDE,請看清單 2 類程式碼:

清單 2. 例項類

package sample.code;

public class UserAccess {
// simple validation for user name and password
public static boolean accessCheck(String userName, String password) {
if (userName.length() <= 4 || userName.length() > 8)
return false;
if (password.length() <= 4 || password.length() > 8)
return false;
if (userName.contains("@"))
return false;
if (password.contains("*"))
return false;
return true;
}
}



回頁首
Feed4JUnit 的下載及安裝
1. Feed4JUnit 是開源的測試元件,您可以從如下連結下載最新版本:
http://sourceforge.net/projects/feed4junit/files/
2. 解壓下載的 zip 包,複製整個 lib 資料夾到您的 Java 專案的根目錄,如圖 1:

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

3. 選定專案,右鍵選擇專案的屬性,然後通過 Add JARs 將步驟 2 中 lib 資料夾下的所有 Jar 新增到專案的 Build Path 下,如圖 2

圖 2. 新增 Jar 到 Build Path

通過以上三步,您已經準備好您的 Feed4JUnit 環境並可以開始使用它,當然,開發測試程式碼之前,您必需要將 Feed4JUnit 相應的包 Import 進您的類。
回頁首
使用 Feed4JUnit 實現資料與程式碼分離的測試
Feed4JUnit 的資料來源可以包括以下幾種型別 - 檔案 (CSV 或者 Excel )、資料庫、自定義資料來源。
Feed4JUnit 使用一個特殊的執行類 Feeder.class,用來支援與標識引數化測試,如果您想要編寫資料與程式碼分離的測試指令碼,必須在您的測試類上增加註釋 @RunWith(Feeder.class) 。同時,您需要使用 @Test 來標示您實現測試的方法,並且使用 @Source 來宣告和接收資料來源的資料,基本的程式碼結構如清單 3 所示:
[img]http://www.ibm.com/developerworks/cn/java/j-lo-feed4junit/image007.jpg[/img]

清單 3. 基本程式碼結構


package sample.test;

import static org.junit.Assert.assertEquals;
import org.databene.feed4junit.Feeder;
import org.databene.benerator.anno.Source;
import org.junit.Test;
import org.junit.runner.RunWith;

/*
* Feed4JUnit - @RunWith, @Test and @Source
*/
@RunWith(Feeder.class) //Specify the class will be ran as Feeder class
public class Feed4JSample {

@Test //Specify the method as a test method
@Source()//Specify the input data source
public void testAccessCheck() {
assertEquals(true, true);
}
}



以檔案作為資料來源
Feed4JUnit 支援從 CSV 或者 Excel 檔案裡面讀取資料作為輸入,這裡我們以 Excel 檔案為例。
1. 在測試專案的根目錄下建立 Data.xls 資料檔案,樣例資料如圖 3,預設情況下,第一行會以列名存在,在執行過程中不會作為資料讀取。

圖 3. Excel 資料來源
[img]http://www.ibm.com/developerworks/cn/java/j-lo-feed4junit/image007.jpg[/img]

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

清單 4. 檔案資料來源示例

package sample.test;

import static org.junit.Assert.assertEquals;

import org.databene.benerator.anno.Source;
import org.databene.feed4junit.Feeder;
import org.junit.Test;
import org.junit.runner.RunWith;

import sample.code.UserAccess;

/*
* Feed4JUnit - Get Data from CSV/Excel File source
*/

@RunWith(Feeder.class)
public class F4JfromFile {

@Test
@Source("Data.csv")//CSV source
public void testAccessCheck_CSV(String userName, String pw, boolean expected) {
assertEquals(expected, UserAccess.accessCheck(userName, pw));
}

@Test
@Source("Data.xls")//Excel source
public void testAccessCheck_Excel(String userName, String pw, boolean expected) {
assertEquals(expected, UserAccess.accessCheck(userName, pw));
}
}



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

圖 4. 執行結果示例
[img]http://www.ibm.com/developerworks/cn/java/j-lo-feed4junit/image009.jpg[/img]

以資料庫作為資料來源
通過使用 @Database ,您可以很方便的使用來自於資料庫的資料,這在進行大量測試資料測試的時候或者複用現有的應用業務資料作為測試資料的情況下比較有用。
當您使用來自資料庫的資料來源的時候,首先必須使用 @Database 宣告資料庫的資訊 ,可以為類或方法新增 @Database 註釋 , 如果註釋類為 @Database 的時候,類中所有的方法都可以使用此資料庫的資料作為源,當宣告 @Database 於方法的時候,此類中僅此方法可以呼叫資料庫作為資料來源。@ Database 具有一些屬性,用於宣告用於連線資料庫資訊,請看一下說明:
id: 一個用於標識資料庫資料來源識別符號,在測試方法的 @Source 中進行引用關聯
url:資料庫的 URL
driver: 資料庫的驅動
user: 資料庫的使用者名稱
password:資料庫的密碼
完成以上資料庫的定義後,需要在測試方法的 @Source 中引用您所需要的庫,使用屬性 id 和 selector 可以完成此操作:
id: @Source 中的 id 和 @Database 的 id 相對應關聯
selector:SQL 語句,用於查詢出相應的資料傳遞給測試方法
以下我們以 DB2 作為資料來源,使用 DB2 的 Sample 資料庫,並建立名為 TEST 的表來儲存測試資料,測試資料與圖 3 Excel 資料來源的完全相同,請看圖 5。
[img]http://www.ibm.com/developerworks/cn/java/j-lo-feed4junit/image011.jpg[/img]

圖 5. 資料表中的測試資料

首先,建立測試類,新增 @Database 註釋並增加資料庫的連線資訊,同時指定一個表示資料庫的 id,在測試方法的 @Source 中通過 id 進行關聯,並制定 selector 的語句進行資料查詢,例如本例中的 selector = "select * from TEST",會從 TEST 表中取出全部資料用於測試,細節請參考以下程式碼示例:
清單 5 為在類上宣告 @Database。

清單 5. 在類上宣告 @Database

package sample.test;

import static org.junit.Assert.assertEquals;

import org.databene.benerator.anno.Database;
import org.databene.benerator.anno.Source;
import org.databene.feed4junit.Feeder;
import org.junit.Test;
import org.junit.runner.RunWith;

import sample.code.UserAccess;

/*
* Feed4JUnit - Get Data from Database, all test methods can use the database data
*/
@RunWith(Feeder.class)
@Database(
id = "testdb",
url = "jdbc:db2://localhost:50000/SAMPLE",
driver = "com.ibm.db2.jcc.DB2Driver",
user = "db2admin",
password = "db2admin")
public class F4JfromDB {

@Test
@Source(id = "testdb", selector = "select * from TEST")
public void testAccessCheck(String userName, String pw, String expected) {
Boolean bSucess = UserAccess.accessCheck(userName.trim(), pw.trim());
assertEquals(expected.trim(), bSucess.toString());

}
}



清單 6 為在方法上宣告 @Database:

清單 6. 在方法上宣告 @Database

/*
* Feed4JUnit - Get Data from Database,
only the specified method can use the database data
*/

@RunWith(Feeder.class)
public class F4JfromDB_Method {
@Test
@Database(
id = "testdb",
url = "jdbc:db2://localhost:50000/SAMPLE",
driver = "com.ibm.db2.jcc.DB2Driver",
user = "db2admin",
password = "db2admin")
@Source(id = "testdb", selector = "select * from TEST")
public void testAccessCheck(String userName, String pw, String expected) {
Boolean bSucess = UserAccess.accessCheck(userName.trim(), pw.trim());
assertEquals(expected.trim(), bSucess.toString());

}
}



測試執行過程中,通過 url,driver 等資訊建立資料連線,通過 selector 發出資料請求,最後完成查詢並把資料傳遞給測試方法,資料在傳遞給方法的時候,會按資料表的列的順序與引數進行匹配,執行結果與圖 4 類似。
自定義資料來源
除了 CSV,Excel 和資料庫的資料來源外,Feed4JUnit 還提供自定義資料來源,以滿足不同使用者的需求,使用者同樣可以通過封裝 JUnit 4 提供的引數化測試的方法來完成資料來源自定義,所有這裡作者不再詳述,使用者可以封裝並取得不同的資料來源的資料,傳遞給 Feed4JUnit 的相應介面,來完成資料來源的自定義。
回頁首
結束語
本文通過對比介紹和簡單易懂的例項全面講解了 Feed4JUnit 對資料與程式碼分離的測試支援。通過提供簡單的註釋,Feed4JUnit 使使用者能夠極其方便的實施資料與程式碼分離的測試,極大地增強了 JUnit 測試框架的易用性。 相信您已經在本文的敘述中看到它的優點。同時,本文所敘述的僅僅是 Feed4JUnit 提供的測試增強功能的一部分,Feed4JUnit 同時還提供了大量資料的隨機測試和等價類測試等眾多功能,如果您感興趣可以自行參考。

相關文章