??? ??? 在做
Java
企業程序的時候,不可避免地要和外部資源打交道,比如數據庫,
Http
請求等。對于這些外部資源的處理,我們可采取的操作或者是直接處理或者是模擬處理。當我們使用
Webwork
,
Spring
,
Hibernate
等框架時,我們要測試的并不僅僅是
Java
代碼,我們還要測試依賴于這些框架的配置文件等等。因此,對于數據持久化的測試,
Mock
方法是行不通的,我們需要真實地測試數據庫操作。對于持久化測試來說,重要的是創造出已知的“干凈的”的準備數據。如果我們在測試一個持久化方法前不能確定數據庫到底存著什么數據,我們只能通過反復地查看數據庫數據來驗證測試方法的正確性了(這就是我和大多數人以前使用的最“直接”的方法)。現在就讓我們使用
DbUnit
,來更好的更自動化的測試持久化操作吧!
??? ??? 先介紹一下
DbUnit
。
DbUnit
是一個
JUnit
擴展,適用于數據驅動的程序。使用
DbUnit
,可以在測試運行期間將數據庫的數據處于已知狀態,這樣在測試時可以方便地寫出測試斷言,也能自動地完成對數據持久化方法的測試。在使用上,
DbUnit
也很簡單,
它提供了大量的類對與數據庫相關的操作進行了抽象和封裝,大多數情況下你只需要使用少量簡單的
API
。
??? ??? 下面我通過一個實際的小例子,介紹一下如何使用
DbUnit
。我也是剛剛使用上了
DbUnit
,經驗上不是很豐富,如果文中有不對的地方,也歡迎指正。這個例子很簡單,我將較為詳細地說明如何使用
Hibernate
和
DbUnit
進行測試。
??? ??? 測試第一步,準備數據集。操作的數據表就是如下的
Account
表(使用的數據庫為Mysql):
create
?
table
?Account(
????id?
bigint
?
not
?
null
?auto_increment,
????name?
varchar
(
50
)?
not
?
null
,
????
primary
?
key
(id)
)
character
?
set
?gbk;
??? ??? 至于
Account
類,映射文件等這里就不給出了。AccountDAO
接口很簡單,只有兩個方法:
public
?
interface
?AccountDAO?{
????
void
?insert(Account?a);
????List
<
Account
>
?findAll();
}
???
??? ??? 實現類如下:
public
?
class
?AccountHibernateDAO?
implements
?AccountDAO{
????
????
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
內容如下:
<?
xml?version='1.0'?encoding='UTF-8'
?>
<
dataset
>
????
<
Account?
id
="1"
?name
="kafka"
/>
????
<
Account?
id
="2"
?name
="0102"
/>
</
dataset
>
? ??
??? ??? 數據集就是一個
xml
文件,
<dataset>
中的每個節點對應的就是一條表數據記錄(一個
dataset文件可以對應多個數據表記錄
)。這里的
<Account>
節點對應的就是
Account
表,屬性就是表中的字段,屬性值就是字段值了。在做測試時,數據集中的內容可以手動敲進去,也可以通過工具將數據庫中的數據導出來。
對于數據集的詳細信息,可參考
http://dbunit.sourceforge.net/components.html#FlatXmlDataSet
。
? ??
??? 測試第二步,擴展
DBTestCase
。
DBTestCase
是繼承自
JUnit
的類,擴展它需要實現
getDataSet()
來提供數據集。
另外,你也可以根據需要擴展繼承于
DBTestCase
的子類
JdbcBasedDBTestCase
,
DataSourceBasedDBTestCas
,
JndiBasedDBTestCase
。下面是繼承于
DBTestCase
的
AccountHibernateDAO
的測試類
AccountHibernateDAOTest
:
package
?hibernatesample.dao.impl;
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
,就是什么也不處理。可選的操作還有幾個,可參考文檔進行設置,但默認的設置是最通用的了。
testDataset
()只是測試數據集中的數據和裝載到數據庫中數據是否一致。
??
??? 通過上面的設置,我們就可以測試dao方法的正確性了,而我們要做的只是準備dataset文件及使用少量的DbUnit
API(可以將這些操作寫到一個抽象類中,測試類都繼承自這個抽象類)。下一篇將介紹整合Spring,Hibernate和DbUnit進行測試。