現實系統中通常會有一些具有外部依賴性的對象
這些對象和數據庫或者其他對象存在諸多關聯
如果我們對這樣的對象編寫單元和組件級測試的話
可以想象將是非常麻煩的一件事
因為這種外部依賴性的存在
使的我們很難將對象孤立出來進行測試
經常提及的白盒測試法
基本上就是通過控制對象的外部依賴性來達到隔離對象的目的
使的可以操作這些對象的狀態和相關行為
運用 模擬對象(mock objects) 或者stubs
就是一個控制對象外部依賴性的解決方案
通過隔離那些關聯的數據庫訪問類
象JDBC的相關操作類
對於控制對象外部依賴性將是很有效的
但模擬對象的解決方案對一些特殊的應用系統架構就顯得力不從心了
象那些運用了EJB的CMP(container
managed
persistence)或者 JDO(java Data
Objects)的應用系統架構
在這些架構裡
數據庫的訪問對象是在最底層的而且很隱蔽
由Manuel Laflamme 編寫的開放源代碼的DBUnit架構體系
對於控制系統內部的數據庫依賴性提供了一個非常不錯的解決方案
他允許程序員在整個的測試過程中自由的管理控制數據庫的狀態
這很重要
利用DBUnit
在測試之前
我們可以給目標數據庫植入我們需要的數據集
而且
在測試完畢後
數據庫完全能夠回溯到測試前的狀態
在很多成功的軟件項目中
測試自動化往往是關鍵的層面
DBUnit允許開發人員創建測試用例代碼
在這些測試用例的生命周期內我們可以很好的控制數據庫的狀態
而且
這些測試用例是很容易實現自動化的
這樣在測試過程中我們無須對它進行人工的干預
為人工干預造成的後果而擔心就更沒必要了
簡單介紹 配置使用DBUnit的第一步我們首先需要知道如何生成數據庫schema
這個文件是XML格式的
其中包括了數據庫的表及相關數據信息
例如
這裡有一個數據庫表employee
我們可以用SQL的形式這樣將他表示出來
而且
我們可以看到
一個簡單的數據集可以這樣表示
在DBUnit中
上面這個表和抽樣數據信息可以用XML文件的形式這樣表示
<EMPLOYEE employee_uid=
start_date=
first_name=
Andrew
ssn=
xxx
xx
xxxx
last_name=
Glover
/>
這個生成的XML格式的文件可以作為系統所需的所有種子文件(seed
files)的樣本模版
為相互關聯的測試場景創建多個種子文件是一個很有效的策略
就象通過不同的數據庫文件來區分隔離數據庫狀態是一個道理
多種子文件策略可以將我們的測試目標鎖定到較小的范圍
目標數據可以只針對數據庫的表
而不是整個數據庫
為了給目標數據庫植入不同的職員記錄
我們需要的XML數據文件如下所示
<?xml version=
encoding=
UTF
?>
<dataset>
<EMPLOYEE employee_uid=
start_date=
first_name=
Drew
ssn=
last_name=
Smith
/>
<EMPLOYEE employee_uid=
start_date=
first_name=
Nick
ssn=
last_name=
Marquiss
/>
<EMPLOYEE employee_uid=
start_date=
first_name=
Jose
ssn=
last_name=
Whitson
/>
</dataset>
現在
要讓DBUnit和我們所需的數據庫schema一起工作了
對於程序員來說
我們使用DBUnit進行測試可以有兩種選擇
通過直接編碼方式進行測試或者與Ant結合
編碼方式 DBUnit框架提供了一個基本的抽象測試用例類
叫做DatabaseTestCase
它是JUnit框架中的基礎類TestCase的子類
如果我們使用這個類必須首先實現兩個鉤子方法(hook
methods)
getConnection()和getDataSet()
方法getConnection()需要返回一個IDatabaseConnection類型的對象
這個對象是一個基於普通JDBC連接的包裝類
例如
下面的代碼段演示了在MySQL數據庫環境下
IDatabaseConnection類型連接對象的創建方法
protected IDatabaseConnection getConnection()
throws Exception {
Class driverClass = Class
forName(
org
gjt
mm
mysql
Driver
);
Connection jdbcConnection = DriverManager
getConnection(
jdbc:mysql://
/hr
hr
hr
);
return new DatabaseConnection(jdbcConnection);
}
方法getDataSet()返回一個IDataSet類型對象
其實
說白了
他就是我們先前提到的XML數據的種子文件的另一種表現形式
protected IDataSet getDataSet() throws Exception {
return new FlatXmlDataSet(
new
FileInputStream(
hr
seed
xml
));
}
有了這兩個基本的方法以後
DBUnit就可以按照它預先缺省的行為工作了
DatabaseTestCase類提供了兩個fixture(我叫它固件
不知仁兄同意否?)方法來控制測試前和測試後的數據庫狀態
這兩個方法就是
getSetUpOperation() 和 getTearDownOperation()
一種高效的實施方案就是讓getSetUpOperation()方法執行REFRESH操作
通過這個操作
我們可以用種子文件中的數據去更新目標數據庫裡的數據
接下來
就是getTearDownOperation()
讓他去執行一個NONE操作
也就是什麼也不執行
protected DatabaseOperation getSetUpOperation()
throws
Exception {
return DatabaseOperation
REFRESH;
}
protected DatabaseOperation getTearDownOperation()
throws
Exception {
return DatabaseOperation
NONE;
}
還有一種有效的方法就是在getSetUpOperation()方法中執行CLEAN_INSERT操作
這樣首先會將目標數據庫中與我們提供的種子文件一致的數據刪除
然後將我們提供的數據插入到數據庫中
這個實施順序保證了我們對數據庫的精確控制
代碼樣例 在一個基於J
EE的人力資源系統中
我們很希望對某個數據操作周期實現測試自動化
這個操作周期包括職員的新增
檢索
更新和刪除
遠程接口定義了下列的業務方法(為了簡潔清楚
省略了方法中的throws子句)
//譯者注
這裡的EmployeeValueObject類型對象
譯者認為是代表職員實體信息的對象
public void createEmployee( EmployeeValueObject emplVo )
public EmployeeValueObject getEmployeeBySocialSecNum( String ssn )
public void updateEmployee( EmployeeValueObject emplVo )
public void deleteEmployee( EmployeeValueObject emplVo )
測試getEmployeeBySocialSecNum()方法
需要植入一條數據到目標數據庫中
另外
測試deleteEmployee()方法和updateEmployee()方法時
同樣也是在先前植入的這條記錄的基礎上進行
最後
測試類會首先利用createEmployee()方法創建一條記錄
同時我們需要校驗執行這個方法時
是否會有異常發生
下面這個DBUnit種子文件
叫做
employee_hr_seed
xml
下面將用到這個文件
<?xml version=
encoding=
UTF
?>
<dataset>
<EMPLOYEE employee_uid=
start_date=
first_name=
Drew
ssn=
last_name=
Smith
/>
<EMPLOYEE employee_uid=
start_date=
first_name=
Nick
ssn=
last_name=
Marquiss
/>
<EMPLOYEE employee_uid=
start_date=
first_name=
Jose
ssn=
last_name=
Whitson
/>
</dataset>
測試類 EmployeeSessionFacadeTest
需要擴展DBUnit的基礎類DatabaseTestCase並且必須提供對getConnection()和getDataSet()方法的實現
在getConnection()方法中將獲得與EJB容器初始化時一樣的數據庫實例
getDataSet()方法負責讀取上面提及的employee_hr_seed
xml文件的數據
測試方法相當簡單
因為DBUnit已經為我們處理了復雜的數據庫生命周期任務
為了測試getEmployeeBySocialSecNum()方法
只需要簡單的傳遞一個存在於種子文件中的社保代碼號即可
比如
//譯者注
EmployeeFacade 類型對象
譯者認為是代表底層數據庫數據的映射體
public void testFindBySSN() throws Exception{
EmployeeFacade facade = //obtain somehow
EmployeeValueObject vo =
facade
getEmployeeBySocialSecNum(
);
TestCase
assertNotNull(
vo shouldn
t be null
vo);
TestCase
assertEquals(
should be Drew
Drew
vo
getFirstName());
TestCase
assertEquals(
should be Smith
Smith
vo
getLastName());
}
為了確保操作周期中的創建職員方法createEmployee()沒有問題
我們只需簡單的執行一下這個方法
然後校驗一下看有沒有異常拋出
另外
下一步我們要做的就是在這條新增的記錄上進行查找操作
看是否可以找到剛創建的記錄
public void testEmployeeCreate() throws Exception{
From:http://tw.wingwit.com/Article/program/Java/JSP/201311/19133.html