DbUnit入門實戰(zhàn)(轉(zhuǎn)載)
相信做過單元測試的人都會對JUnit非常的熟悉了,今天要介紹的DbUnit(http://dbunit.sourceforge.net/)則是專門針對數(shù)據(jù)庫測試的對JUnit的一個擴(kuò)展,它可以將測試對象數(shù)據(jù)庫置于一個測試輪回之間的狀態(tài)。鑒于目前國內(nèi)介紹DbUnit的系統(tǒng)教程比較少見,本文將分從理論和實例兩個方面帶你領(lǐng)略DbUnit的精彩世界。
DbUnit設(shè)計理念
熟悉單元測試的開發(fā)人員都知道,在對數(shù)據(jù)庫進(jìn)行單元測試時候,通常采用的方案有運用模擬對象(mock objects)和stubs兩種。通過隔離關(guān)聯(lián)的數(shù)據(jù)庫訪問類,比如JDBC的相關(guān)操作類,來達(dá)到對數(shù)據(jù)庫操作的模擬測試。然而某些特殊的系統(tǒng),比如利用了EJB的CMP(container-managed persistence)的系統(tǒng),數(shù)據(jù)庫的訪問對象是在最底層而且很隱蔽的,那么這兩種解決方案對這些系統(tǒng)就顯得力不從心了。
DBUnit的設(shè)計理念就是在測試之前,備份數(shù)據(jù)庫,然后給對象數(shù)據(jù)庫植入我們需要的準(zhǔn)備數(shù)據(jù),最后,在測試完畢后,讀入備份數(shù)據(jù)庫,回溯到測試前的狀態(tài);
而且又因為DBUnit是對JUnit的一種擴(kuò)展,開發(fā)人員可以通過創(chuàng)建測試用例代碼,在這些測試用例的生命周期內(nèi)來對數(shù)據(jù)庫的操作結(jié)果進(jìn)行比較。
DbUnit測試基本概念和流程
基于DbUnit 的測試的主要接口是IDataSet。IDataSet代表一個或多個表的數(shù)據(jù)。
可以將數(shù)據(jù)庫模式的全部內(nèi)容表示為單個IDataSet 實例。這些表本身由Itable 實例來表示。
IDataSet 的實現(xiàn)有很多,每一個都對應(yīng)一個不同的數(shù)據(jù)源或加載機(jī)制。最常用的幾種 IDataSet實現(xiàn)為:
FlatXmlDataSet:數(shù)據(jù)的簡單平面文件 XML 表示
QueryDataSet:用 SQL 查詢獲得的數(shù)據(jù)
DatabaseDataSet:數(shù)據(jù)庫表本身內(nèi)容的一種表示
XlsDataSet :數(shù)據(jù)的excel表示
一般而言,使用DbUnit進(jìn)行單元測試的流程如下:
1 根據(jù)業(yè)務(wù),做好測試用的準(zhǔn)備數(shù)據(jù)和預(yù)想結(jié)果數(shù)據(jù),通常準(zhǔn)備成xml格式文件。
2 在setUp()方法里邊備份數(shù)據(jù)庫中的關(guān)聯(lián)表。
3 在setUp()方法里邊讀入準(zhǔn)備數(shù)據(jù)。
4 對測試類的對應(yīng)測試方法進(jìn)行實裝:執(zhí)行對象方法,把數(shù)據(jù)庫的實際執(zhí)行結(jié)果和預(yù)想結(jié)果進(jìn)行比較。
5 在tearDown()方法里邊,把數(shù)據(jù)庫還原到測試前狀態(tài)。
DbUnit開發(fā)實例
下面通過一個實例來說明DbUnit的實際運用。
實例準(zhǔn)備
比如有一個學(xué)生表[student],結(jié)構(gòu)如下:
--------------------------------------------------------------------------------
id char(4) pk 學(xué)號
name char(50) 姓名
sex char(1) 性別
birthday date 出生日期
--------------------------------------------------------------------------------
準(zhǔn)備數(shù)據(jù)如下:
--------------------------------------------------------------------------------
id name sex birthday
0001 翁仔 m 1979-12-31
0002 王翠花 f 1982-08-09
--------------------------------------------------------------------------------
測試對象類為StudentOpe.java,里邊有2個方法:
findStudent(String id) :根據(jù)主鍵id找記錄
addStudent(Student student) :添加一條記錄
在測試addStudent方法時候,我們準(zhǔn)備添加如下一條數(shù)據(jù)
--------------------------------------------------------------------------------
id name sex birthday
0088 王耳朵 m 1982-01-01
--------------------------------------------------------------------------------
那么在執(zhí)行該方法后,數(shù)據(jù)庫的student表里的數(shù)據(jù)是這樣的:
--------------------------------------------------------------------------------
id name sex birthday
0001 翁仔 m 1979-12-31
0002 王翠花 f 1982-08-09
0088 王耳朵 m 1982-01-01
--------------------------------------------------------------------------------
然后我們說明如何對這2個方法進(jìn)行單元測試。
實例展開
1 把準(zhǔn)備數(shù)據(jù)和預(yù)想數(shù)據(jù)轉(zhuǎn)換成xml文件
student_pre.xml
--------------------------------------------------------------------------------
<?xml version='1.0' encoding="gb2312"?>
<dataset>
<student id="0001" name="翁仔" sex="m" birthday="1979-12-31"/>
<student id="0002" name="王翠花" sex="f" birthday="1982-08-09"/>
</dataset>
--------------------------------------------------------------------------------
student_exp.xml
--------------------------------------------------------------------------------
<?xml version='1.0' encoding="gb2312"?>
<dataset>
<student id="0001" name="翁仔" sex="m" birthday="1979-12-31"/>
<student id="0002" name="王翠花" sex="f" birthday="1982-08-09"/>
<student id="0088" name="王耳朵" sex="m" birthday="1982-01-01"/>
</dataset>
--------------------------------------------------------------------------------
2 實裝setUp方法,詳細(xì)見代碼注釋。
--------------------------------------------------------------------------------
protected void setUp() {
IDatabaseConnection connection =null;
try{
super.setUp();
//本例使用postgresql數(shù)據(jù)庫
Class.forName("org.postgresql.Driver");
//連接DB
Connection conn=DriverManager.getConnection("jdbc:postgresql:testdb.test","postgres","postgres");
//獲得DB連接
connection =new DatabaseConnection(conn);
//對數(shù)據(jù)庫中的操作對象表student進(jìn)行備份
QueryDataSet backupDataSet = new QueryDataSet(connection);
backupDataSet.addTable("student");
file=File.createTempFile("student_back",".xml");//備份文件
FlatXmlDataSet.write(backupDataSet,new FileOutputStream(file));
//準(zhǔn)備數(shù)據(jù)的讀入
IDataSet dataSet = new FlatXmlDataSet( new FileInputStream("student_pre.xml"));
DatabaseOperation.CLEAN_INSERT.execute(connection,dataSet);
}catch(Exception e){
e.printStackTrace();
}finally{
try{
if(connection!=null) connection.close();
}catch(SQLException e){}
}
}
--------------------------------------------------------------------------------
3 實裝測試方法,詳細(xì)見代碼注釋。
*檢索類方法,可以利用assertEquals()方法,拿表的字段進(jìn)行比較。
--------------------------------------------------------------------------------
// findStudent
public void testFindStudent() throws Exception{
//執(zhí)行findStudent方法
StudentOpe studentOpe=new StudentOpe();
Student result = studentOpe.findStudent("0001");
//預(yù)想結(jié)果和實際結(jié)果的比較
assertEquals("翁仔",result.getName());
assertEquals("m",result.getSex());
assertEquals("1979-12-31",result.getBirthDay());
}
--------------------------------------------------------------------------------
*更新,添加,刪除等方法,可以利用Assertion.assertEquals()方法,拿表的整體來比較。
--------------------------------------------------------------------------------
public void testAddStudent() throws Exception{
//執(zhí)行addStudent方法
StudentOpe studentOpe=new StudentOpe();
//被追加的記錄
Student newStudent = new Student("0088","王耳朵","m","1982-01-01");
//執(zhí)行追加方法
Student result = studentOpe.addStudent(newStudent);
//預(yù)想結(jié)果和實際結(jié)果的比較
IDatabaseConnection connection=null;
try{
//預(yù)期結(jié)果取得
IDataSet expectedDataSet = new FlatXmlDataSet(new FileInputStream("student_exp.xml"));
ITable expectedTable = expectedDataSet.getTable("student");
//實際結(jié)果取得
Connection conn=getConnection();
connection =new DatabaseConnection(conn);
IDataSet databaseDataSet = connection.createDataSet();
ITable actualTable = databaseDataSet.getTable("student");
//比較
Assertion.assertEquals(expectedTable, actualTable);
}finally{
if(connection!=null) connection.close();
}
}
--------------------------------------------------------------------------------
*如果在整體比較表的時候,有個別字段不需要比較,可以用DefaultColumnFilter.excludedColumnsTable()方法,
將指定字段給排除在比較范圍之外。比如上例中不需要比較birthday這個字段的話,那么可以如下代碼所示進(jìn)行處理:
--------------------------------------------------------------------------------
ITable filteredExpectedTable = DefaultColumnFilter.excludedColumnsTable(expectedTable, new String[]{"birthday"});
ITable filteredActualTable = DefaultColumnFilter.excludedColumnsTable(actualTable,new String[]{"birthday"});
Assertion.assertEquals(filteredExpectedTable, filteredActualTable);
--------------------------------------------------------------------------------
4 在tearDown()方法里邊,把數(shù)據(jù)庫還原到測試前狀態(tài)
--------------------------------------------------------------------------------
protected void tearDown() throws Exception{
IDatabaseConnection connection =null;
try{
super.tearDown();
Connection conn=getConnection();
connection =new DatabaseConnection(conn);
IDataSet dataSet = new FlatXmlDataSet(file);
DatabaseOperation.CLEAN_INSERT.execute(connection,dataSet);
}catch(Exception e){
e.printStackTrace();
}finally{
try{
if(connection!=null) connection.close();
}catch(SQLException e){}
}
}
DbUnit設(shè)計理念
熟悉單元測試的開發(fā)人員都知道,在對數(shù)據(jù)庫進(jìn)行單元測試時候,通常采用的方案有運用模擬對象(mock objects)和stubs兩種。通過隔離關(guān)聯(lián)的數(shù)據(jù)庫訪問類,比如JDBC的相關(guān)操作類,來達(dá)到對數(shù)據(jù)庫操作的模擬測試。然而某些特殊的系統(tǒng),比如利用了EJB的CMP(container-managed persistence)的系統(tǒng),數(shù)據(jù)庫的訪問對象是在最底層而且很隱蔽的,那么這兩種解決方案對這些系統(tǒng)就顯得力不從心了。
DBUnit的設(shè)計理念就是在測試之前,備份數(shù)據(jù)庫,然后給對象數(shù)據(jù)庫植入我們需要的準(zhǔn)備數(shù)據(jù),最后,在測試完畢后,讀入備份數(shù)據(jù)庫,回溯到測試前的狀態(tài);
而且又因為DBUnit是對JUnit的一種擴(kuò)展,開發(fā)人員可以通過創(chuàng)建測試用例代碼,在這些測試用例的生命周期內(nèi)來對數(shù)據(jù)庫的操作結(jié)果進(jìn)行比較。
DbUnit測試基本概念和流程
基于DbUnit 的測試的主要接口是IDataSet。IDataSet代表一個或多個表的數(shù)據(jù)。
可以將數(shù)據(jù)庫模式的全部內(nèi)容表示為單個IDataSet 實例。這些表本身由Itable 實例來表示。
IDataSet 的實現(xiàn)有很多,每一個都對應(yīng)一個不同的數(shù)據(jù)源或加載機(jī)制。最常用的幾種 IDataSet實現(xiàn)為:
FlatXmlDataSet:數(shù)據(jù)的簡單平面文件 XML 表示
QueryDataSet:用 SQL 查詢獲得的數(shù)據(jù)
DatabaseDataSet:數(shù)據(jù)庫表本身內(nèi)容的一種表示
XlsDataSet :數(shù)據(jù)的excel表示
一般而言,使用DbUnit進(jìn)行單元測試的流程如下:
1 根據(jù)業(yè)務(wù),做好測試用的準(zhǔn)備數(shù)據(jù)和預(yù)想結(jié)果數(shù)據(jù),通常準(zhǔn)備成xml格式文件。
2 在setUp()方法里邊備份數(shù)據(jù)庫中的關(guān)聯(lián)表。
3 在setUp()方法里邊讀入準(zhǔn)備數(shù)據(jù)。
4 對測試類的對應(yīng)測試方法進(jìn)行實裝:執(zhí)行對象方法,把數(shù)據(jù)庫的實際執(zhí)行結(jié)果和預(yù)想結(jié)果進(jìn)行比較。
5 在tearDown()方法里邊,把數(shù)據(jù)庫還原到測試前狀態(tài)。
DbUnit開發(fā)實例
下面通過一個實例來說明DbUnit的實際運用。
實例準(zhǔn)備
比如有一個學(xué)生表[student],結(jié)構(gòu)如下:
--------------------------------------------------------------------------------
id char(4) pk 學(xué)號
name char(50) 姓名
sex char(1) 性別
birthday date 出生日期
--------------------------------------------------------------------------------
準(zhǔn)備數(shù)據(jù)如下:
--------------------------------------------------------------------------------
id name sex birthday
0001 翁仔 m 1979-12-31
0002 王翠花 f 1982-08-09
--------------------------------------------------------------------------------
測試對象類為StudentOpe.java,里邊有2個方法:
findStudent(String id) :根據(jù)主鍵id找記錄
addStudent(Student student) :添加一條記錄
在測試addStudent方法時候,我們準(zhǔn)備添加如下一條數(shù)據(jù)
--------------------------------------------------------------------------------
id name sex birthday
0088 王耳朵 m 1982-01-01
--------------------------------------------------------------------------------
那么在執(zhí)行該方法后,數(shù)據(jù)庫的student表里的數(shù)據(jù)是這樣的:
--------------------------------------------------------------------------------
id name sex birthday
0001 翁仔 m 1979-12-31
0002 王翠花 f 1982-08-09
0088 王耳朵 m 1982-01-01
--------------------------------------------------------------------------------
然后我們說明如何對這2個方法進(jìn)行單元測試。
實例展開
1 把準(zhǔn)備數(shù)據(jù)和預(yù)想數(shù)據(jù)轉(zhuǎn)換成xml文件
student_pre.xml
--------------------------------------------------------------------------------
<?xml version='1.0' encoding="gb2312"?>
<dataset>
<student id="0001" name="翁仔" sex="m" birthday="1979-12-31"/>
<student id="0002" name="王翠花" sex="f" birthday="1982-08-09"/>
</dataset>
--------------------------------------------------------------------------------
student_exp.xml
--------------------------------------------------------------------------------
<?xml version='1.0' encoding="gb2312"?>
<dataset>
<student id="0001" name="翁仔" sex="m" birthday="1979-12-31"/>
<student id="0002" name="王翠花" sex="f" birthday="1982-08-09"/>
<student id="0088" name="王耳朵" sex="m" birthday="1982-01-01"/>
</dataset>
--------------------------------------------------------------------------------
2 實裝setUp方法,詳細(xì)見代碼注釋。
--------------------------------------------------------------------------------
protected void setUp() {
IDatabaseConnection connection =null;
try{
super.setUp();
//本例使用postgresql數(shù)據(jù)庫
Class.forName("org.postgresql.Driver");
//連接DB
Connection conn=DriverManager.getConnection("jdbc:postgresql:testdb.test","postgres","postgres");
//獲得DB連接
connection =new DatabaseConnection(conn);
//對數(shù)據(jù)庫中的操作對象表student進(jìn)行備份
QueryDataSet backupDataSet = new QueryDataSet(connection);
backupDataSet.addTable("student");
file=File.createTempFile("student_back",".xml");//備份文件
FlatXmlDataSet.write(backupDataSet,new FileOutputStream(file));
//準(zhǔn)備數(shù)據(jù)的讀入
IDataSet dataSet = new FlatXmlDataSet( new FileInputStream("student_pre.xml"));
DatabaseOperation.CLEAN_INSERT.execute(connection,dataSet);
}catch(Exception e){
e.printStackTrace();
}finally{
try{
if(connection!=null) connection.close();
}catch(SQLException e){}
}
}
--------------------------------------------------------------------------------
3 實裝測試方法,詳細(xì)見代碼注釋。
*檢索類方法,可以利用assertEquals()方法,拿表的字段進(jìn)行比較。
--------------------------------------------------------------------------------
// findStudent
public void testFindStudent() throws Exception{
//執(zhí)行findStudent方法
StudentOpe studentOpe=new StudentOpe();
Student result = studentOpe.findStudent("0001");
//預(yù)想結(jié)果和實際結(jié)果的比較
assertEquals("翁仔",result.getName());
assertEquals("m",result.getSex());
assertEquals("1979-12-31",result.getBirthDay());
}
--------------------------------------------------------------------------------
*更新,添加,刪除等方法,可以利用Assertion.assertEquals()方法,拿表的整體來比較。
--------------------------------------------------------------------------------
public void testAddStudent() throws Exception{
//執(zhí)行addStudent方法
StudentOpe studentOpe=new StudentOpe();
//被追加的記錄
Student newStudent = new Student("0088","王耳朵","m","1982-01-01");
//執(zhí)行追加方法
Student result = studentOpe.addStudent(newStudent);
//預(yù)想結(jié)果和實際結(jié)果的比較
IDatabaseConnection connection=null;
try{
//預(yù)期結(jié)果取得
IDataSet expectedDataSet = new FlatXmlDataSet(new FileInputStream("student_exp.xml"));
ITable expectedTable = expectedDataSet.getTable("student");
//實際結(jié)果取得
Connection conn=getConnection();
connection =new DatabaseConnection(conn);
IDataSet databaseDataSet = connection.createDataSet();
ITable actualTable = databaseDataSet.getTable("student");
//比較
Assertion.assertEquals(expectedTable, actualTable);
}finally{
if(connection!=null) connection.close();
}
}
--------------------------------------------------------------------------------
*如果在整體比較表的時候,有個別字段不需要比較,可以用DefaultColumnFilter.excludedColumnsTable()方法,
將指定字段給排除在比較范圍之外。比如上例中不需要比較birthday這個字段的話,那么可以如下代碼所示進(jìn)行處理:
--------------------------------------------------------------------------------
ITable filteredExpectedTable = DefaultColumnFilter.excludedColumnsTable(expectedTable, new String[]{"birthday"});
ITable filteredActualTable = DefaultColumnFilter.excludedColumnsTable(actualTable,new String[]{"birthday"});
Assertion.assertEquals(filteredExpectedTable, filteredActualTable);
--------------------------------------------------------------------------------
4 在tearDown()方法里邊,把數(shù)據(jù)庫還原到測試前狀態(tài)
--------------------------------------------------------------------------------
protected void tearDown() throws Exception{
IDatabaseConnection connection =null;
try{
super.tearDown();
Connection conn=getConnection();
connection =new DatabaseConnection(conn);
IDataSet dataSet = new FlatXmlDataSet(file);
DatabaseOperation.CLEAN_INSERT.execute(connection,dataSet);
}catch(Exception e){
e.printStackTrace();
}finally{
try{
if(connection!=null) connection.close();
}catch(SQLException e){}
}
}
posted on 2010-10-09 14:20 都市淘沙者 閱讀(320) 評論(0) 編輯 收藏 所屬分類: Hibernate/ORM