關於DBunit
百度百科:dbunit是一個基於junit擴展的數據庫測試框架。
簡言之,dbunit是爲了在單元測試中,通過備份數據庫、導入測試數據、回滾恢復數據庫的手段,在不污染數據庫的前提下完成一系列單元測試工作。
DBunit關鍵知識點
IDataSet
接口:用於操作表集合
ITable
接口:用於操作表數據集合
DatabaseOperation
類:對錶數據執行一系列操作,比如刷新、刪除、插入等
DBunit操作流程
在沒使用DBunit做單元測試之前,一般遵循junit的測試流程:
- @BeforeClass在加載測試類之前執行初始化操作
- @Before執行單元測試方法前執行操作
- @Test執行測試邏輯
- @After執行單元測試方法後執行操作
- @AfterClass在測試類結束後操作
加入DBunit實際上只是在junit的流程中添加幾步操作數據庫的動作:
- @BeforeClass構建連接數據源(DataSource)
- @Before創建連接,備份數據並且插入測試數據
- @Test執行測試邏輯
- @After測試完成還原數據
- @AfterClass關閉連接
操作的流程不是固定的,具體還是要根據測試的邏輯來決定,如果能夠加入事務特性進行單元測試可以大大精簡流程
Springboot集成DBunit步驟
進入正題,這裏使用的版本是:
- DBunit 2.5.3
- Springboot 2
一、引用依賴
<!-- SpringBoot 測試 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- dbunit包 -->
<dependency>
<groupId>org.dbunit</groupId>
<artifactId>dbunit</artifactId>
<version>2.5.3</version>
<scope>test</scope>
</dependency>
二、初始化表&創建測試數據
初始化SQL腳本:
CREATE TABLE `db_unit_test` (
`id` bigint(20) NOT NULL,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
測試數據XML:
<?xml version='1.0' encoding='UTF-8'?>
<dataset>
<db_unit_test id="1" name="張三"/>
<db_unit_test id="2" name="李四"/>
<db_unit_test id="3" name="王五"/>
</dataset>
三、封裝DBunit方法
AbstractBaseTest
封裝DBunit基本操作方法,包含初始化連接、關閉連接方法,備份數據、還原數據、導入數據和清空數據方法。其中AbstractBaseTest
繼承了AbstractTransactionalJUnit4SpringContextTests
類的事務特性,可以方便使用事務回滾特性實現數據庫0污染。
參考:https://www.cnblogs.com/wade-xu/p/4547381.html
/**
* <pre>
* 單元測試基類
* 封裝DBunit相關操作方法
* </pre>
* https://www.cnblogs.com/wade-xu/p/4547381.html
* @author oyf
*/
@RunWith(SpringJUnit4ClassRunner.class)
@Transactional
//繼承AbstractTransactionalJUnit4SpringContextTests會在方法執行完成後進行事務回滾,如果需要不回滾事務需要在方法上加上 @Rollbak(false)
public abstract class AbstractBaseTest extends AbstractTransactionalJUnit4SpringContextTests {
@Autowired
private DataSource dataSource;
/**
* 數據庫連接對象
*/
private static IDatabaseConnection conn;
/**
* 備份文件
*/
private File tempFile;
/**
* 文件跟目錄
*/
public static final String ROOT_URL = "src/test/resources/";
// DBunit方法 --------------------------------------------------------
/**
* 獲取數據庫連接
* @throws Exception
*/
@Before
public void setup() throws Exception {
//get DataBaseSourceConnection
conn = new DatabaseConnection(DataSourceUtils.getConnection(dataSource));
}
/**
* 關閉數據庫連接
* @throws Exception
*/
//這裏不能用@After,當測試方法有@Rollback(false)註解時會在事務沒有結束之前關閉了數據庫連接
@AfterTransaction
public void teardown() throws Exception {
if (conn != null) {
conn.close();
}
}
/**
* Get Query DataSet
*
* @Title: getQueryDataSet
* @return
* @throws SQLException
*/
protected QueryDataSet getQueryDataSet() throws SQLException {
return new QueryDataSet(conn);
}
/**
* 備份表數據
*
* @Title: backupCustom
* @param tableName
* @throws Exception
*/
protected void backupCustom(String... tableName){
try {
// back up specific files
QueryDataSet qds = getQueryDataSet();
for (String str : tableName) {
qds.addTable(str);
}
tempFile = new File(ROOT_URL+"temp.xml");
FlatXmlDataSet.write(qds, new FileWriter(tempFile), "UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 清空表數據,並導入測試數據
* @throws Exception
*/
public void importTables(String file){
try {
IDataSet dataSet = new FlatXmlDataSetBuilder().build(new File(ROOT_URL+file));
DatabaseOperation.CLEAN_INSERT.execute(conn, dataSet);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 回滾數據
*
* @Title: rollback
* @throws Exception
*/
protected void rollback(){
try {
// get the temp file
FlatXmlDataSetBuilder builder = new FlatXmlDataSetBuilder();
builder.setColumnSensing(true);
IDataSet ds =builder.build(new FileInputStream(tempFile));
// recover database
DatabaseOperation.CLEAN_INSERT.execute(conn, ds);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 清空表數據
*
* @param tableName
* @throws Exception
*/
protected void clearTable(String tableName){
try {
DefaultDataSet dataset = new DefaultDataSet();
dataset.addTable(new DefaultTable(tableName));
DatabaseOperation.DELETE_ALL.execute(conn, dataset);
}catch (Exception e) {
e.printStackTrace();
}
}
}
四、準備業務代碼
主要是準備查詢數據用的service和dao,具體代碼可以在https://gitee.com/oumuv/h2TestDemo下載
五、編寫測試類
這裏簡單演示了兩種方式實現數據庫隔離測試,test1()方法用spring的事務特性,test2()使用DBunit手動回滾數據
/**
* 測試demo
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = H2Application.class)
public class TestDemo2 extends AbstractBaseTest{
@Autowired
DBunitService dBunitService;
/**
* 使用事務回滾機制,自動還原數據庫
*/
@Test
public void test1() {
//導入測試數據
importTables("dbunit-data.xml");
List<DBunitEntity> list = dBunitService.list();
Assert.assertNotNull(list);
}
/**
* 加上註解 @Rollback(false) 不使用事務回滾,手動還原數據庫
*/
@Test
@Rollback(false)
public void test2() {
//備份數據,備份數據在resources/temp.xml
backupCustom("db_unit_test");
//導入測試數據
importTables("dbunit-data.xml");
List<DBunitEntity> list = dBunitService.list();
Assert.assertNotNull(list);
//手動恢復數據
rollback();
}
}
運行測試用例發現查出來的數據確實是xml中的測試數據,而數據庫中的數據並沒有被污染也沒有缺失,結果便是成功了!
具體代碼可在https://gitee.com/oumuv/h2TestDemo自行下載