Feed4JUnit 與 JUnit
經常,在應用程序的業務邏輯中存在大量的這樣的接口:他們接受不同的輸入,然後進行 或驗證,或處理,進而完成相同的流程。比如網站的登錄入口,用戶名和密碼都有長度的限制,同時也具有是否允許特殊字 符的限制等,所以在我們進行其單元測試的過程中,根據不同長度的用戶名和密碼,以及不同的字符組合,只需要提供相同 的測試代碼結構,就能完成測試,不同的僅僅測試數據與期望值,但是因為每一個測試方法中的輸入參數不同,我們必須為 每一個輸入組編寫單獨的測試用例,從而產生大量冗余代碼,十分不便於維護。
基於以上場景,JUnit 4 提供了參 數化的特性,從而能夠實現不同數據輸入對相同測試代碼的測試,如清單 1 所示:
清單 1. JUnit 4 參數化測試代 碼示例
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 所示:
清單 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 數據源
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. 運行結果示例
以數據 庫作為數據源
通過使用 @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。
圖 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 同時還提供了大量數據的隨 機測試和等價類測試等眾多功能,如果您感興趣可以自行參考。
下載