1. 程式人生 > >在Spring中結合Dbunit對Dao進行整合單元測試

在Spring中結合Dbunit對Dao進行整合單元測試

Java程式碼 收藏程式碼
  1. package com.test.dbunit.dao;  
  2. import javax.sql.DataSource;  
  3. import org.dbunit.Assertion;  
  4. import org.dbunit.database.DatabaseConnection;  
  5. import org.dbunit.database.IDatabaseConnection;  
  6. import org.dbunit.database.QueryDataSet;  
  7. import org.dbunit.dataset.IDataSet;  
  8. import org.dbunit.dataset.xml.FlatXmlDataSet;  
  9. import org.dbunit.operation.DatabaseOperation;  
  10. import org.junit.Assert;  
  11. import org.junit.Before;  
  12. import org.junit.Test;  
  13. import org.springframework.beans.factory.annotation.Autowired;  
  14. import org.springframework.core.io.ClassPathResource;  
  15. import org.springframework.jdbc.datasource.DataSourceUtils;  
  16. import org.springframework.test.context.ContextConfiguration;  
  17. import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;  
  18. import org.springframework.test.context.transaction.TransactionConfiguration;  
  19. import com.test.dbunit.entity.User;  
  20. @ContextConfiguration
    (locations = { "classpath:testApplicationContext.xml" })  
  21. @TransactionConfiguration(defaultRollback = true)  
  22. publicclass UserDaoTest extends AbstractTransactionalJUnit4SpringContextTests {  
  23.     @Autowired
  24.     private UserDao userDao;  
  25.     @Autowired
  26.     private DataSource dataSource;  
  27.     private IDatabaseConnection conn;  
  28.     @Before
  29.     publicvoid initDbunit() throws Exception {  
  30.         conn = new DatabaseConnection(DataSourceUtils.getConnection(dataSource));  
  31.     }  
  32.     @Test
  33.     publicvoid saveUser() throws Exception {  
  34.         User user = new User();  
  35.         user.setNick("user001");  
  36.         user.setPassword("password001");  
  37.         userDao.save(user);  
  38.         QueryDataSet actual = new QueryDataSet(conn);  
  39.         actual.addTable("user",  
  40.                 "select * from user where user.nick = 'user001'");  
  41.         IDataSet expected = new FlatXmlDataSet(new ClassPathResource(  
  42.                 "com/taobao/dbunit/dao/user001.xml").getFile());  
  43.         Assertion.assertEquals(expected, actual);  
  44.     }  
  45.     @Test
  46.     publicvoid updateUser() throws Exception {  
  47.         IDataSet origen = new FlatXmlDataSet(new ClassPathResource(  
  48.                 "com/taobao/dbunit/dao/user001.xml").getFile());  
  49.         DatabaseOperation.INSERT.execute(conn, origen);  
  50.         User user = new User();  
  51.         user.setNick("user001");  
  52.         user.setPassword("password002");  
  53.         userDao.update(user);  
  54.         QueryDataSet actual = new QueryDataSet(conn);  
  55.         actual.addTable("user",  
  56.                 "select * from user where user.nick = 'user001'");  
  57.         IDataSet expected = new FlatXmlDataSet(new ClassPathResource(  
  58.                 "com/taobao/dbunit/dao/user001_updated.xml").getFile());  
  59.         Assertion.assertEquals(expected, actual);  
  60.     }  
  61.     @Test
  62.     publicvoid removeUser() throws Exception {  
  63.         IDataSet origen = new FlatXmlDataSet(new ClassPathResource(  
  64.                 "com/taobao/dbunit/dao/user001.xml").getFile());  
  65.         DatabaseOperation.INSERT.execute(conn, origen);  
  66.         userDao.remove("user001");  
  67.         QueryDataSet actual = new QueryDataSet(conn);  
  68.         actual.addTable("user""select * from user where nick = 'user001'");  
  69.         Assert.assertEquals(0, actual.getTable("user").getRowCount());  
  70.     }  
  71.     @Test
  72.     publicvoid findUser() throws Exception {  
  73.         IDataSet data = new FlatXmlDataSet(new ClassPathResource(  
  74.                 "com/taobao/dbunit/dao/user001.xml").getFile());  
  75.         DatabaseOperation.INSERT.execute(conn, data);  
  76.         User user = userDao.getUserByNick("user001");  
  77.         Assert.assertEquals("password001", user.getPassword());  
  78.     }  
  79. }  

 對Dao進行單元測試,一般有兩種思路。一是Mock,對使用的底層API進行Mock,比如Hibernate和JDBC介面,判斷介面有沒有正確呼叫,另一種是實際訪問資料庫,判斷資料庫有沒有正確讀寫。更多的情況下,我更傾向於後者,因為在使用ORM工具或者jdbcTemplate的情況下,dao一般只有簡單的幾行程式碼,沒有複雜的邏輯,Mock測試一般沒有什麼意義,我們更關心的是,Hibernate mapping是否正確,ibatis sql是否正確等,所以實際讀寫資料庫才能真正判斷一個dao是否正確,這也是我們關心的測試內容。

好的單元測試應該是原子性的,獨立的,不應依賴其他測試和上下文,但是要測試資料讀寫是否正確,就必須涉及初始資料的載入,資料修改的還原等操作。對於初始資料的載入,手動輸入很麻煩,一個解決方案就是使用Dbunit,從Xml檔案甚至Excel中載入初始資料到資料庫,是資料庫的值達到一個已知狀態。同時還可以使用Dbunit,對資料庫的結果狀態進行判斷,保證和期望的一致。資料修改的還原,可以依賴Spring TransactionalTests,在測試完成後回滾資料庫。

Dbunit還可以對資料的現有資料進行備份,還原,清空現有資料,一個好的測試實踐是每一個開發人員一個測試資料庫,進而對資料庫的資料狀態有更好的控制,但現實可能會是共享同一個測試庫,所以這種情況下,測試的編寫必須多做一些考慮。

待測試的類:

Java程式碼 收藏程式碼
  1. package com.test.dbunit.dao.impl;  
  2. import java.sql.ResultSet;  
  3. import java.sql.SQLException;  
  4. import org.springframework.jdbc.core.RowMapper;  
  5. import com.test.dbunit.dao.UserDao;  
  6. import com.test.dbunit.entity.User;  
  7. publicclass DefaultUserDao extends BaseDao implements UserDao {  
  8.     privatestatic String QUERY_BY_NICK = "select * from user where user.nick = ?";  
  9.     privatestatic String REMOVE_USER = "delete from user where user.nick = ?";  
  10.     privatestatic String INSERT_USER = "insert into user(nick,password) values(?, ?)";  
  11.     privatestatic String UPDATE_USER = "update user set user.password = ? where user.nick = ?";  
  12.     @Override
  13.     public User getUserByNick(String nick) {  
  14.         return (User) getJdbcTemplate().queryForObject(QUERY_BY_NICK,new Object[]{nick}, new RowMapper(){  
  15.             @Override
  16.             public Object mapRow(ResultSet rs, int index) throws SQLException {  
  17.                 User user = new User();  
  18.                 user.setNick(rs.getString("nick"));  
  19.                 user.setPassword(rs.getString("password"));  
  20.                 return user;  
  21.             }  
  22.         });  
  23.     }  
  24.     @Override
  25.     publicvoid remove(String nick) {  
  26.         getJdbcTemplate().update(REMOVE_USER, new Object[]{nick});  
  27.     }  
  28.     @Override
  29.     publicvoid save(User user) {  
  30.         getJdbcTemplate().update(INSERT_USER, new Object[]{user.getNick(), user.getPassword()});  
  31.     }  
  32.     @Override
  33.     publicvoid update(User user) {  
  34.         getJdbcTemplate().update(UPDATE_USER, new Object[]{user.getPassword(), user.getNick()});  
  35.     }  
  36. }  

 單元測試:

需要注意的地方就是,DataSourceUtils.getConnection(datasource)通過這種方式獲得資料庫連線初始化Dbunit,能夠保證Dbunit使用的資料連線和當前事務的資料庫連線相同,保證能夠在參與到事務中。Spring的TransactionManager會在開始事務時把當前連線儲存到ThreadLocal中,DataSourceUtils.getConnection方法,首先從ThreadLocal中獲取連線。 

user001.xml

Xml程式碼 收藏程式碼
  1. <?xmlversion="1.0"encoding="UTF-8"?>
  2. <dataset>
  3.     <usernick="user001"password="password001"/>
  4. </dataset>

 使用dbunit,可以通過xml檔案定義資料集,也可以使用其他方式定義,比如Excel,程式設計方式。

Dbunit的主要構件

IDatabaseConnection

IDataSet

資料集,資料集可以從Xml檔案Excel等外部檔案獲取,也可以從資料庫查詢,或者程式設計方式構件,資料集可以作為初始資料插入到資料庫,也可以作為斷言的依據。另外還有IDatatable等輔助類。

比如在updateUser測試中,使用了QueryDataSet,從資料庫中構建一個Dataset,再通過FlatXmlDataSet從Xml檔案中構建一個Dataset,斷言這兩個Dataset相同。

Java程式碼 收藏程式碼
  1. QueryDataSet actual = new QueryDataSet(conn);  
  2. actual.addTable("user""select * from user where user.nick = 'user001'");  
  3. IDataSet expected = new FlatXmlDataSet(new ClassPathResource(  
  4.     "com/taobao/dbunit/dao/user001_updated.xml").getFile());  
  5. Assertion.assertEquals(expected, actual);  

DatabaseOperation 

通過定義的靜態欄位可以獲取一組代表一個數據操作的子類物件,比如DatabaseOperation.INSERT,返回 InsertOperation,通過執行execute方法把資料集插入到資料庫。例如:

Java程式碼 收藏程式碼
  1. IDataSet origen = new FlatXmlDataSet(new ClassPathResource(  
  2.                 "com/taobao/dbunit/dao/user001.xml").getFile());  
  3. DatabaseOperation.INSERT.execute(conn, origen);  

 從Xml檔案中構建DataSet,使用Insert插入到資料庫,初始化測試資料。

Assertion

唯一的方法,assertEqual,斷言兩個資料集或資料表相同。

PS:使用Oracle的時候,初始化DatabaseConnection需要傳入scheme。new DatabaseConnection(conn,SCHEMA_NAME) ,SCHMEA_NAME需要大寫。

 附件提供所有程式碼下載

一個DAO測試基類

Java程式碼 收藏程式碼
  1. package com.taobao.dbunit.dao;  
  2. import java.sql.SQLException;  
  3. import javax.sql.DataSource;  
  4. import org.dbunit.Assertion;  
  5. 相關推薦

    Spring結合DbunitDao進行整合單元測試

    Java程式碼  package com.test.dbunit.dao;   import javax.sql.DataSource;   import org.dbunit.Assertion;   import org.dbunit.

    SpringController、Service、Dao進行Junit單元測試總結

    Spring對Controller、Service、Dao進行Junit單元測試總結 [email protected]事務控制,避免資料庫出現髒資料(若要提交到資料庫,先注掉) 2.hibernate配置檔案 <property name="defaultAutoComm

    Spring Boot 2 實踐記錄之 使用 Powermock、Mockito UUID 進行 mock 單元測試

    alt 生成 digest md5 加密 調用 uuid tid 第一步 加密算 由於註冊時,需要對輸入的密碼進行加密,使用到了 UUID、sha1、md 等算法。在單元測試時,使用到了 Powermock,記錄如下。 先看下加密算法: import org.apache

    springbootshiro進行mock單元測試

      環境:junit-5、Spring5.0.x、Spring Boot 2.0.x     以下是用來許可權測試的介面: @ApiOperation("[可接入]分頁查詢管理員") @ApiResponses({@ApiResponse(code

    Spring整合Spring MVC及Mybatis進行Junit單元測試

    我們可以在不啟動服務的情況下,進行單元測試,以便提交出高質量的程式碼。本文以一個小例子,說明在Spring中如何進行單元測試。 一:測試Controller 1:在pom.xml檔案中引入相關依賴 <properties> <!-- 設定專案編碼編碼 --&

    在VS2017使用XlslibExcel進行操作

    /* 2018-10-12 16:57:05 使用xlslib來對Excel檔案的一些操作 VS2017編譯的一些問題    (這個庫只支援寫) */ 詳細的編譯過程見 參考:https://www.cnblogs.com/dongc/p/8256813.html

    Python使用numpy序列進行離散傅立葉變換DFT

    看了大佬對DFT的介紹後感覺離散傅立葉變換對序列訊號的處理還是很有用的, 總結下來就是DFT可以增加有限長序列的長度來提高物理解析度。 自己用python中的numpy庫實現了一下: 其中繪相簿的使用請參考:Python繪圖 將有效長度為4的單位序列,變換為長度16的DFT譜線。

    Spring整合JUnit4進行AOP單元測試的時候,報:"C:\Program Files\Java\jdk1.8.0_191\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:C

    錯誤程式碼 "C:\Program Files\Java\jdk1.8.0_191\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA

    spring boot MongoTemplate如何 mongodb 進行模糊查詢

    以下為部分程式碼片段,供參考。 1: 程式碼片段1,用Pattern方式來實現 不區分大小寫的匹配(包括精確和模糊匹配) //完全匹配 Pattern pattern = Pattern.compile("^張$", Pattern.CASE_INSENSITIVE);

    Spring實現後臺JSON傳的陣列物件給List型別的引數繫結

    就是在後臺接收前端傳的JSON串,其中包含陣列型別的資料。例如傳一個user使用者列表,後臺用List來接收。 其實有多種方法都可以實現,大概就是JSON方式,和非JSON的key/value那種方法。但是後臺前輩告訴我,要考慮前臺不是所有都會這兩種傳參的方式,

    在Node基於MongooseMongoDB進行增刪查改(CRUD)操作(一)

    關鍵詞:mongodb安裝 mongoose使用 robomongo mongoose的CRUD操作 mongoose的查詢,增加,修改,刪除 工具介紹 MongoDB MongoDB是基於Javascript語言的資料庫,儲存格式是JSON,而N

    JavaLamda表示式List進行排序

    public class MainTest { public static void main(String[] args) { List<String> list = Arrays.asList( "2018121207","2018121206","2018121

    在entity framework 使用 LINQ 進行左關聯查詢且group by 分組查詢的示例,並且按小時分組查詢時間段

    有表RealTimeDatas的欄位RecordTime儲存了實時時間,格式為DateTime 現在需要以小時進行分組統計每個時間段的最大值,最小值,和平均值 同時,另一個表Devices中有標準溫度溼度最大最小值範圍,需要將這個結果一併關聯到查詢結果中    

    Scikit-Learn(sklearn)的KNeighborsClassifier鳶尾花進行分類

    案例 from sklearn import datasets from sklearn.model_selection import train_test_split from sklearn.neighbors import KNeighborsClassifier iris =

    shelldate命令month進行加減操作的bug

    在大部分情況下這個命令執行正確。但是有些情況下這個命令會出現問題,比如當前日期是3月30、3月31、5月31等的時候,上面的命令得到的結果還是當月,而不是我們期望的上月。 這個問題是怎麼產生的呢?比如噹噹前日期是5月31時,-1 month它只是將月份-1,就得到4月31,但是實際上4月只有30天,所以

    在storyboard或者xib使用autolayoutUIScrollView進行佈局需要注意的問題

    為了方便描述問題,我們從一個簡單的例子開始說起 首先在storyboard中拖入UIScrollView設定它的Leading Trailing Top 再給定一個高度height為200,再簡單不

    MFC利用ADO資料庫進行簡單操作的例項

    目標:實現對資料庫中的資料進行簡單地操作,包括增、刪、改、查。 第一步,建立一個基於對話方塊的應用程式,命名為TestAdo 第二步,按照圖1的介面佈局新增控制元件 圖1 程式介面示意圖 第三步,程式碼實現      首先,要用#import語句來引用支援ADO的

    jmeter利用BeanShell時間進行復雜處理程式碼參考

    1)生成格式化的時間,並設定生成的時間是在當前時間往後推幾天 import java.util.Date; import java.text.DateFormat; Date date=new Date();//取時間 Calendar calendar =

    從資料庫取出資料,存放到陣列,然後資料進行分類顯示

    <table width="280" id="zhangdan_wenzi" > <?php //food_typ id 將id存到陣列中 $sqlfood = "SELECT food_type.id FROM food_type ";

    利用Arrays類的方法陣列進行排序

    package com.hongdou.java; import java.util.Arrays; public class MathDemo { public static void ma