【概念】
dbunit是一個基於junit擴展的數據庫測試框架。它提供了大量的類對與數據庫相關的操作進行了抽象和封裝,雖然在80%的情況,你只需使用它極少的api。它通過使用用戶自定義的數據集以及相關操作使數據庫處於一種可知的狀態,從而使得測試自動化、可重複和相對獨立。雖然不用dbunit也可以達到這種目的,但是我們必須爲此付出代價(編寫大量代碼,測試及維護),既然有了這麼優秀的開源框架,我們又何必再造輪子。
DbUnit是爲數據庫驅動的項目提供的一個對JUnit 的擴展,除了提供一些常用功能,它可以將你的數據庫置於一個測試輪迴之間的狀態。
【簡介】
爲依賴於其他外部系統(如數據庫或其他接口)的代碼編寫單元測試是一件很困難的工作。在這種情況下,有效的單元必須隔離測試對象和外部依賴,以便管理測試對象的狀態和行爲。
使用mock object對象,是隔離外部依賴的一個有效方法。如果我們的測試對象是依賴於DAO的代碼,mock object技術很方便。但如果測試對象變成了DAO本身,又如何進行單元測試呢?
開源的DbUnit項目,爲以上的問題提供了一個相當優雅的解決方案。使用DbUnit,開發人員可以控制測試數據庫的狀態。進行一個DAO單元測試之前,DbUnit爲數據庫準備好初始化數據;而在測試結束時,DbUnit會把數據庫狀態恢復到測試前的狀態。
【原理】
dbunit的與單元測試相關的兩個最重要的核心是org.dbunit.database.IDatabaseConnection 和 org.dbunit.dataset.IDataSet ,前者是產品代碼使用的數據庫連接的一個簡單的封裝,後者是對單元測試人員自定義的數據集(通常以xml文件的形式存在,且xml文件的格式也有好幾種)的封裝。
還有一個很重要的咚咚就是org.dbunit.operation.DatabaseOperation,該類是一個抽象類代表了對數據庫的操作,例如CUD以及其組合等, 它採用了退化的工廠模式,可直接通過它獲取其具體的子類(代表具體的某種操作)如下:
DatabaseOperation.UPDATE
DatabaseOperation.DELETE
DatabaseOperation.DELETE_ALL
DatabaseOperation.TRUNCATE
DatabaseOperation.REFRESH
DatabaseOperation.CLEAN_INSERT
DatabaseOperation.NONE
工作流程如下:
1)testcase.setup--->testcase.getConnection-->getDataSet----->operation.execute(通常DatabaseOperation.CLEAN_INSERT)
2)testcase.testSomeMethod---->dao.someMethod
3)testcase.teardown---->operation.execute(通常DatabaseOperation.DELETE_ALL或者DatabaseOperation.NONE)
下面是一個Step by Step的教程
1、導入jar包
2、建數據庫
create database junit_db default character set UTF8;
use junit_db;
create table j_user(username varchar(50), password varchar(50), nickname varchar(50));
3、創建實體
package com.lxh.model;
public class User {
private String username;
private String password;
private String nickname; ....
}
4、創建數據庫連接的工具類
package com.lxh.util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class DbUtil {
public static Connection getConnection() throws SQLException {
Connection con = null;
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/junit_db?useUnicode=true&characterEncoding=utf8", "root", "root");
return con;
}
public static void close(Connection con) {
try {
if (con != null) {
con.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void close(PreparedStatement ps) {
try {
if (ps != null) {
ps.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void close(ResultSet rs) {
try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
5、創建dbunit的測試數據xml文件,並將文件存儲在類路徑下的dbunit_xml/j_user.xml中(這個路徑隨便放,只要在讀取文件的時候能讀取到就行)
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<j_user username="admin" password="123" nickname="管理員"/>
</dataset>
dbunit的XML屬性支持兩種格式的描述:基於XML節點的數據描述;基於XML屬性的數據描述。這裏展示的是基於XML屬性的數據描述。
6、在測試中dbunit的基本使用步驟:(1)、備份數據庫 (2)、導入測試基本數據 (3)、還原數據庫 (4)、執行測試案例,接下來就按照這個順序一一展示dbunit的神祕
6.1、備份數據庫
6.1.1、備份整個數據庫
@Test
public void testBackupAllTable() {
try {
// jdbc的數據庫連接
Connection conn = DbUtil.getConnection();
// 創建dbunit的Connection,需要傳入數據庫連接
IDatabaseConnection jconn = new DatabaseConnection(conn);
// 從dbunit的連接中獲取數據集,因爲是從數據庫連接中獲取的數據集,所以數據集就是整個數據庫
IDataSet ds = jconn.createDataSet();
// 將整個數據集中的數據導出到E:\\db_backup.xml文件中,並設置編碼爲UTF-8
FlatXmlDataSet.write(ds, new BufferedWriter(new OutputStreamWriter(new FileOutputStream("E:\\db_backup.xml"), "UTF-8")));
} catch (DataSetException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (DatabaseUnitException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
將這個備份數據的操作做成一個TestCase了,這樣就實現了整個數據庫的備份,數據的所有數據將會備份到E:\\db_backup.xml文件中。
6.1.2、備份制定的數據庫表
@Test
public void testBackUpTable() {
try {
// jdbc的數據庫連接
Connection conn = DbUtil.getConnection();
// 創建dbunit的Connection,需要傳入數據庫連接
IDatabaseConnection jconn = new DatabaseConnection(conn);
// 通過QueryDataSet可以有效的選擇要處理的表作爲數據集
QueryDataSet backup = new QueryDataSet(jconn);
// 添加j_user這張表作爲備份表
backup.addTable("j_user");
FlatXmlDataSet.write(backup, new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream("E:\\db_backup.xml"), "UTF-8")));
} catch (AmbiguousTableNameException e) {
e.printStackTrace();
} catch (DataSetException e) {
e.printStackTrace();
} catch (DatabaseUnitException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
同時可以添加多張表,這樣就能實現備份指定的表了
6.2、導入測試基本數據
@Test
public void testLoad() {
try {
// jdbc的數據庫連接
Connection conn = DbUtil.getConnection();
// 創建dbunit的Connection,需要傳入數據庫連接
IDatabaseConnection jconn = new DatabaseConnection(conn);
// FlatXmlDataSet是用來獲取基於屬性存儲的屬性值 XMLDataSet用來獲取基於節點類型存儲的屬性值
IDataSet ds = new FlatXmlDataSet(
new FlatXmlProducer(
new InputSource(
new BufferedReader(
new InputStreamReader(
this.getClass().getClassLoader().getResourceAsStream("dbunit_xml/j_user.xml"), "UTF-8")
)
)
)
);
// 將數據的數據清空,並且把測試數據插入
DatabaseOperation.CLEAN_INSERT.execute(jconn, ds);
} catch (DataSetException e) {
e.printStackTrace();
} catch (DatabaseUnitException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
通過這個方法,我們就可以把E:\\db_backup.xml中的數據作爲基本數據先導入到數據,爲單元測試做準備數據
6.3、還原數據庫
在進行完我們的所有測試工作後,我們需要將數據庫還原,這個時候就要用到我們備份數據庫時生成的備份XML文件了
@Test
public void testResume() {
try {
// jdbc的數據庫連接
Connection conn = DbUtil.getConnection();
// 創建dbunit的Connection,需要傳入數據庫連接
IDatabaseConnection jconn = new DatabaseConnection(conn);
// 根據備份文件創建dataset
IDataSet ds = new FlatXmlDataSet(
new FlatXmlProducer(
new InputSource(
new BufferedReader(
new InputStreamReader(
new FileInputStream("E:\\db_backup.xml"), "UTF-8")))));
DatabaseOperation.CLEAN_INSERT.execute(jconn, ds);
} catch (DataSetException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (DatabaseUnitException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
將e://db_backup.xml文件重新導入數據庫,就實現了數據庫的還原操作。
6.4、通過以上幾個步驟的操作,我們現在已經可以寫做一個完成整的與數據庫隔離的測試代碼了
@Test
public void testDbUnit() throws Exception {
// 備份數據庫
testBackUpTable();
// 加載測試數據
testLoad();
// 進行測試
IUserDao ud = new UserDaoImpl();
User tu = ud.load("admin");
assertEquals(tu.getUsername(), "admin");
assertEquals(tu.getPassword(), "123");
assertEquals(tu.getNickname(), "管理員");
// 還原數據庫
testResume();
}