??? ??? 在做 Java 企業程序的時候,不可避免地要和外部資源打交道,比如數據庫, Http 請求等。對于這些外部資源的處理,我們可采取的操作或者是直接處理或者是模擬處理。當我們使用 Webwork , Spring , Hibernate 等框架時,我們要測試的并不僅僅是 Java 代碼,我們還要測試依賴于這些框架的配置文件等等。因此,對于數據持久化的測試, Mock 方法是行不通的,我們需要真實地測試數據庫操作。對于持久化測試來說,重要的是創造出已知的“干凈的”的準備數據。如果我們在測試一個持久化方法前不能確定數據庫到底存著什么數據,我們只能通過反復地查看數據庫數據來驗證測試方法的正確性了(這就是我和大多數人以前使用的最“直接”的方法)。現在就讓我們使用 DbUnit ,來更好的更自動化的測試持久化操作吧!
??? ??? 先介紹一下 DbUnit 。 DbUnit 是一個 JUnit 擴展,適用于數據驅動的程序。使用 DbUnit ,可以在測試運行期間將數據庫的數據處于已知狀態,這樣在測試時可以方便地寫出測試斷言,也能自動地完成對數據持久化方法的測試。在使用上, DbUnit 也很簡單, 它提供了大量的類對與數據庫相關的操作進行了抽象和封裝,大多數情況下你只需要使用少量簡單的 API 。
??? ??? 下面我通過一個實際的小例子,介紹一下如何使用 DbUnit 。我也是剛剛使用上了 DbUnit ,經驗上不是很豐富,如果文中有不對的地方,也歡迎指正。這個例子很簡單,我將較為詳細地說明如何使用 Hibernate 和 DbUnit 進行測試。
??? ??? 測試第一步,準備數據集。操作的數據表就是如下的 Account 表(使用的數據庫為Mysql):
????id?
bigint
?
not
?
null
?auto_increment,
????name?
varchar
(
50
)?
not
?
null
,
????
primary
?
key
(id)
)
character
?
set
?gbk;
??? ??? 至于 Account 類,映射文件等這里就不給出了。AccountDAO 接口很簡單,只有兩個方法:
????
void
?insert(Account?a);
????List
<
Account
>
?findAll();
}
???
??? ??? 實現類如下:
????
???? public ? void ?insert(Account?a){
????????Session?s? = ?HibernateSessionFactory.getSession();
????????s.save(a);
????????s.close();
????}
????
???? public ?List < Account > ?findAll(){
????????Session?s? = ??HibernateSessionFactory.getSession();
????????List < Account > ?l? = ?(List < Account > )s.createCriteria(Account. class ).list();
????????s.close();
???????? return ?l;
????}
}
??? ??? 在測試前,要準備出數據表中要裝入的數據(也就是數據集),這里給出與Account表對應的數據集文件 Accout.xml 內容如下:
<
dataset
>
????
<
Account?
id
="1"
?name
="kafka"
/>
????
<
Account?
id
="2"
?name
="0102"
/>
</
dataset
>
? ??
??? ??? 數據集就是一個
xml
文件,
<dataset>
中的每個節點對應的就是一條表數據記錄(一個
dataset文件可以對應多個數據表記錄
)。這里的
<Account>
節點對應的就是
Account
表,屬性就是表中的字段,屬性值就是字段值了。在做測試時,數據集中的內容可以手動敲進去,也可以通過工具將數據庫中的數據導出來。
對于數據集的詳細信息,可參考
http://dbunit.sourceforge.net/components.html#FlatXmlDataSet
。
? ??
import
?hibernatesample.domain.Account;
import
?hibernatesample.util.HibernateSessionFactory;
import
?java.io.File;
import
?java.io.InputStream;
import
?java.util.List;
import
?org.dbunit.Assertion;
import
?org.dbunit.DBTestCase;
import
?org.dbunit.PropertiesBasedJdbcDatabaseTester;
import
?org.dbunit.dataset.IDataSet;
import
?org.dbunit.dataset.ITable;
import
?org.dbunit.dataset.xml.FlatXmlDataSet;
import
?org.dbunit.operation.DatabaseOperation;
public
?
class
?AccountHibernateDAOTest?
extends
?DBTestCase?{
????
private
?AccountHibernateDAO?accountDAO;
????
????
public
?AccountHibernateDAOTest(){
????????System.setProperty(?PropertiesBasedJdbcDatabaseTester.DBUNIT_DRIVER_CLASS,?HibernateSessionFactory.getDriverClass());
???System.setProperty(?PropertiesBasedJdbcDatabaseTester.DBUNIT_CONNECTION_URL,?HibernateSessionFactory.getConnectionURL());
???System.setProperty(?PropertiesBasedJdbcDatabaseTester.DBUNIT_USERNAME,HibernateSessionFactory.getUsername());
???System.setProperty(?PropertiesBasedJdbcDatabaseTester.DBUNIT_PASSWORD,HibernateSessionFactory.getPassword());
????}
????
????@Override
????
protected
?IDataSet?getDataSet()?
throws
?Exception?{
????????String?path?
=
?
"
hibernatesample
"
+
File.separator
+
"
dao
"
+
File.separator
+
"
dataset
"
+
File.separator
+
"
Account.xml
"
;
????????InputStream?in?
=
?
this
.getClass().getClassLoader().getResourceAsStream(path);
????????
return
?
new
?FlatXmlDataSet(in);
????}
????
????@Override
????
protected
?DatabaseOperation?getSetUpOperation()?
throws
?Exception?{
????????
return
?DatabaseOperation.CLEAN_INSERT;
????}
????@Override
????
protected
?DatabaseOperation?getTearDownOperation()?
throws
?Exception?{
????????
return
?DatabaseOperation.NONE;
????}
????
protected
?
void
?setUp()?
throws
?Exception?{
????????
super
.setUp();
????????accountDAO?
=
?
new
?AccountHibernateDAO();
????}
????
public
?
void
?testInsert()?{
????????Account?a?
=
?
new
?Account();
????????a.setName(
"
aa
"
);
????????accountDAO.insert(a);
????????List
<
Account
>
?l?
=
?accountDAO.findAll();
????????assertEquals(
3
,?l.size());
????????Account?b?
=
?l.get(
2
);
????????assertEquals(
"
aa
"
,?b.getName());
????}
????
public
?
void
?testFindAll()?{
????????List
<
Account
>
?l?
=
?accountDAO.findAll();
????????assertEquals(
2
,?l.size());
????????Account?a?
=
?l.get(
0
);
????????assertEquals(
new
?Long(
1
),?a.getId());
????????assertEquals(
"
kafka
"
,?a.getName());
????????Account?b?
=
?l.get(
1
);
????????assertEquals(
new
?Long(
2
),?b.getId());
????????assertEquals(
"
0102
"
,?b.getName());
????}
????
public
?
void
?testDataset()?
throws
?Exception?{
????????IDataSet?databaseDataSet?
=
?getConnection().createDataSet();
????????ITable?actualTable?
=
?databaseDataSet.getTable(
"
Account
"
);
????????IDataSet?expectedDataSet?
=
?getDataSet();
????????ITable?expectedTable?
=
?expectedDataSet.getTable(
"
Account
"
);
????????Assertion.assertEquals(expectedTable,?actualTable);
????}
}
??? ??? 上面的DBTestCase 依賴于 IDatabaseTester 接口完成工作,而 PropertiesBasedJdbcDatabaseTester 就是其使用的默認實現, AccountHibernateDAOTest 構造函數的作用是完成數據庫連接參數的設置。 protected IDataSet getDataSet() 實現了裝載數據集到 IDataSet 中。 getSetUpOperation 和 getTearDownOperation 是可選的方法,返回的 DatabaseOperation 為 DBTestCase 在 SetUp 和 TearDown 中將執行的操作, getSetUpOperation 默認的操作為 DatabaseOperation.CLEAN_INSERT ,也就是先清空數據表中的數據再插入數據集中的數據到數據表中。getTearDownOperation 默認的操作為 DatabaseOperation.NONE ,就是什么也不處理??蛇x的操作還有幾個,可參考文檔進行設置,但默認的設置是最通用的了。 testDataset ()只是測試數據集中的數據和裝載到數據庫中數據是否一致。