相信作過單元測試的人都會對JUnit 很是的熟悉了,今天要介紹的DbUnit(http://dbunit.sourceforge.net/ ) 則是專門針對數據庫測試的對JUnit 的一個擴展,它能夠將測試對象數據庫置於一個測試輪迴之間的狀態。鑑於目前國內介紹DbUnit 的系統教程比較少見,本文將分從理論和實例兩個方面帶你領略DbUnit 的精彩世界。java
DbUnit 設計理念
熟悉單元測試的開發人員都知道,在對數據庫進行單元測試時候,一般採用的方案有運用模擬對象(mock objects)和stubs 兩種。經過隔離關聯的數據庫訪問類,好比JDBC 的相關操做類,來達到對數據庫操做的模擬測試。然而某些特殊的系統,好比利用了EJB 的CMP(container-managed persistence) 的系統,數據庫的訪問對象是在最底層並且很隱蔽的,那麼這兩種解決方案對這些系統就顯得力不從心了。sql
DBUnit 的設計理念就是在測試以前,備份數據庫,而後給對象數據庫植入咱們須要的準備數據,最後,在測試完畢後,讀入備份數據庫,回溯到測試前的狀態;
並且又由於DBUnit 是對JUnit 的一種擴展,開發人員能夠經過建立測試用例代碼,在這些測試用例的生命週期內來對數據庫的操做結果進行比較。數據庫
DbUnit 測試基本概念和流程
基於DbUnit 的測試的主要接口是IDataSet 。IDataSet 表明一個或多個表的數據。
能夠將數據庫模式的所有內容表示爲單個IDataSet 實例。這些表自己由Itable 實例來表示。
IDataSet 的實現有不少,每個都對應一個不一樣的數據源或加載機制。最經常使用的幾種 IDataSet 實現爲:
FlatXmlDataSet :數據的簡單平面文件 XML 表示
QueryDataSet :用 SQL 查詢得到的數據
DatabaseDataSet :數據庫表自己內容的一種表示
XlsDataSet :數據的excel 表示oracle
通常而言,使用DbUnit 進行單元測試的流程以下:
1 根據業務,作好測試用的準備數據和預想結果數據,一般準備成xml 格式文件。
2 在setUp() 方法裏邊備份數據庫中的關聯表。
3 在setUp() 方法裏邊讀入準備數據。
4 對測試類的對應測試方法進行實裝: 執行對象方法,把數據庫的實際執行結果和預想結果進行比較。
5 在tearDown() 方法裏邊, 把數據庫還原到測試前狀態。post
DbUnit 開發實例
下面經過一個實例來講明DbUnit 的實際運用。單元測試
實例準備
好比有一個學生表[student] ,結構以下:測試
--------------------------------------------------------------------------------
id char(4) pk 學號
name char(50) 姓名
sex char(1) 性別
birthday date 出生日期url
--------------------------------------------------------------------------------
準備數據以下:spa
--------------------------------------------------------------------------------
id name sex birthday
0001 翁仔 m 1979-12-31
0002 王翠花 f 1982-08-09
--------------------------------------------------------------------------------
測試對象類爲StudentOpe.java ,裏邊有2 個方法:
findStudent(String id) : 根據主鍵id 找記錄
addStudent(Student student) :添加一條記錄.net
在測試addStudent 方法時候,咱們準備添加以下一條數據
--------------------------------------------------------------------------------
id name sex birthday
0088 王耳朵 m 1982-01-01
--------------------------------------------------------------------------------
那麼在執行該方法後,數據庫的student 表裏的數據是這樣的:
--------------------------------------------------------------------------------
id name sex birthday
0001 翁仔 m 1979-12-31
0002 王翠花 f 1982-08-09
0088 王耳朵 m 1982-01-01
--------------------------------------------------------------------------------
而後咱們說明如何對這2 個方法進行單元測試。
實例展開
1 把準備數據和預想數據轉換成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 方法,詳細見代碼註釋。
--------------------------------------------------------------------------------
protected void setUp() {
IDatabaseConnection connection =null;
try{
super.setUp();
// 本例使用postgresql 數據庫
Class.forName("org.postgresql.Driver");
// 鏈接DB
Connection conn=DriverManager.getConnection("jdbc:postgresql:testdb.test","postgres","postgres");
// 得到DB 鏈接
connection =new DatabaseConnection(conn);
// 對數據庫中的操做對象表student 進行備份
QueryDataSet backupDataSet = new QueryDataSet(connection);
backupDataSet.addTable("student");
file=File.createTempFile("student_back",".xml");// 備份文件
FlatXmlDataSet.write(backupDataSet,new FileOutputStream(file));
// 準備數據的讀入
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 實裝測試方法,詳細見代碼註釋。
* 檢索類方法,能夠利用assertEquals() 方法,拿表的字段進行比較。
--------------------------------------------------------------------------------
// findStudent
public void testFindStudent() throws Exception{
// 執行findStudent 方法
StudentOpe studentOpe=new StudentOpe();
Student result = studentOpe.findStudent("0001");
// 預想結果和實際結果的比較
assertEquals(" 翁仔",result.getName());
assertEquals("m",result.getSex());
assertEquals("1979-12-31",result.getBirthDay());
}
--------------------------------------------------------------------------------
* 更新,添加,刪除等方法,能夠利用Assertion.assertEquals() 方法,拿表的總體來比較。
--------------------------------------------------------------------------------
public void testAddStudent() throws Exception{
// 執行addStudent 方法
StudentOpe studentOpe=new StudentOpe();
// 被追加的記錄
Student newStudent = new Student("0088"," 王耳朵","m","1982-01-01");
// 執行追加方法
Student result = studentOpe.addStudent(newStudent);
// 預想結果和實際結果的比較
IDatabaseConnection connection=null;
try{
// 預期結果取得
IDataSet expectedDataSet = new FlatXmlDataSet(new FileInputStream("student_exp.xml"));
ITable expectedTable = expectedDataSet.getTable("student");
// 實際結果取得
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 這個字段的話,那麼能夠以下代碼所示進行處理:
--------------------------------------------------------------------------------
ITable filteredExpectedTable = DefaultColumnFilter.excludedColumnsTable(expectedTable, new String[]{"birthday"});
ITable filteredActualTable = DefaultColumnFilter.excludedColumnsTable(actualTable,new String[]{"birthday"});
Assertion.assertEquals(filteredExpectedTable, filteredActualTable);
--------------------------------------------------------------------------------
4 在tearDown() 方法裏邊, 把數據庫還原到測試前狀態
--------------------------------------------------------------------------------
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當作是測試數據庫的東西(其實原本也就是),最近在研究Appfuse是 時候發現Dbunit對數據庫的數據進行load和export很是方便,尤爲是在自動填充數據庫,或者導出數據的時候(兩個能夠反向進行了),或者是 WebTest測試的時候,尤其重要了,簡單的幾句話,就能完成數據的裝載,導出,或者是查詢了,下面有個例子能夠說明這狀況.不過注意的一點就是,配置 路徑了.在Eclipse中在設置Ant的ClassPath時候就要把Dbunit和數據庫的驅動程序Jar包加進去,而後別的都經過下面的例子就OK 了 <project name="SimpleTest" basedir="." default="load"> <property name="dbDriver" value="oracle.jdbc.driver.OracleDriver" /> <property name="dbUrl" value="jdbc:oracle:thin:@192.168.104.47:1521:esample" /> <property name="dbUser" value="esample" /> <property name="dbPassword" value="esample" /> <taskdef name="dbunit" classname="org.dbunit.ant.DbUnitTask" /> <target name="load" description="Loads the database with sample data"> <property name="operation" value="CLEAN_INSERT" /> <property name="file" value="partial.xml" /> <dbunit driver="${dbDriver}" url="${dbUrl}" userid="${dbUser}" password="${dbPassword}"> <operation type="${operation}" src="${file}" format="xml" /> </dbunit> </target> <target name="export"> <dbunit driver="${dbDriver}" url="${dbUrl}" userid="${dbUser}" password="${dbPassword}"> <export dest="partial.xml" format="xml"> <query name="QueryExhibtion" sql="SELECT Exhibition_Id FROM Ex_exhibition " /> <table name="ex_exhibition" /> </export> </dbunit> </target></project> 要先執行export,這樣就會自動生成一個數據導出文件,若是有的話就會覆蓋,而後在用load方法就可讓數據庫加載剛纔生成的那些數據了,具體的加 載方式要設置dbunit中的operation屬性了,有UPDATE, INSERT, DELETE, DELETE_ALL, REFRESH, CLEAN_INSERT, MSSQL_INSERT, MSSQL_REFRESH, MSSQL_CLEAN_INSERT等參數了 這樣的話項目在持續集成的時候就方便多了,關於數據庫的東西都是有Dbunit自動生成了,也算是Xp方法的一個數據庫的實踐把.注dbunit的地址是:http://www.dbunit.org/,上面的例子是在DBunit2.1中測試經過.