天天看點

Java單元測試Junit(二)使用DBUnit擴充JUnit

在我們使用JUnit單元測試架構編寫單元測試的時候,少不免要對資料庫進行操作,但請試想一下,當我要編寫一個擷取使用者的單元測試時,資料庫是不存在該記錄的,那麼我要測試擷取使用者時就需要往資料庫添加一條使用者記錄,但當擷取使用者的單元測試完成并成功後,此測試并沒有清理現場(删除插入資料庫的記錄),那樣當我們再有單元測試需要插入記錄時,就會造成ID沖突的情況,少量的單元測試還可以避免此種情況,但當單元測試的資料龐大時,就會出現各種各樣的問題,而DBUnit可以幫助我們解決這類型的問題

  1. <dependency> 
  2.     <groupId>org.dbunit</groupId> 
  3.     <artifactId>dbunit</artifactId> 
  4.     <version>2.4.9</version> 
  5.     <scope>test</scope> 
  6. </dependency> 
  7. <dependency> 
  8.     <groupId>org.slf4j</groupId> 
  9.     <artifactId>slf4j-api</artifactId> 
  10.     <version>1.7.5</version> 
  11.     <scope>compile</scope> 
  12. </dependency> 

      在Maven管理的項目src/test/resource/下建立default-data.xml 

  1. <?xml version="1.0" encoding="UTF-8"?> 
  2. <dataset> 
  3.     <!-- 注意,此處的user代表表名(database table name) --> 
  4.     <user id="1" username="123" password="456"/> 
  5.     <user id="2" username="123" password="456"/> 
  6.     <user id="3" username="123" password="456"/> 
  7.     <user id="4" username="123" password="456"/> 
  8.     <user id="5" username="123" password="456"/> 
  9. </dataset> 

      以下代碼為提取後的BaseTest 

  1. package com.accentrix.ray;  
  2. import java.io.File;  
  3. import java.io.FileInputStream;  
  4. import java.io.FileWriter;  
  5. import java.io.InputStream;  
  6. import javax.sql.DataSource;  
  7. import org.dbunit.database.DatabaseDataSourceConnection;  
  8. import org.dbunit.database.IDatabaseConnection;  
  9. import org.dbunit.database.QueryDataSet;  
  10. import org.dbunit.dataset.DataSetException;  
  11. import org.dbunit.dataset.IDataSet;  
  12. import org.dbunit.dataset.xml.FlatXmlDataSet;  
  13. import org.dbunit.dataset.xml.FlatXmlProducer;  
  14. import org.dbunit.operation.DatabaseOperation;  
  15. import org.junit.AfterClass;  
  16. import org.junit.BeforeClass;  
  17. import org.xml.sax.InputSource;  
  18. public class BaseTest {  
  19.     public static IDatabaseConnection connection;  
  20.     public static DataSource dataSource;  
  21.     private File temp;  
  22.     @BeforeClass 
  23.     public static void init() throws Exception {  
  24.         // 通過DataSource擷取DataBaseSourceConnection  
  25.         connection = new DatabaseDataSourceConnection(dataSource);  
  26.     }  
  27.     @AfterClass 
  28.     public static void destroy() throws Exception {  
  29.         if (connection != null) {  
  30.             connection.close();  
  31.         }  
  32.     }  
  33.     protected IDataSet getDataSet(String name) throws DataSetException {  
  34.         // 通過類加載器擷取default-data.xml檔案的内容  
  35.         InputStream is = this.getClass().getClassLoader()  
  36.                 .getResourceAsStream(name + ".xml");  
  37.         // IDataSet就類似是一個資料的容器,把default-data.xml内容  
  38.         // 轉換成了DBUnit可識别的資料傳回出去  
  39.         return new FlatXmlDataSet(new FlatXmlProducer(new InputSource(is)));  
  40.     }  
  41.     protected void backupAll() throws Exception {  
  42.         // createDataSet代表從資料庫中擷取到DataSet,此時DataSet為資料庫的内容  
  43.         IDataSet ds = connection.createDataSet();  
  44.         // 建立臨時檔案  
  45.         temp = File.createTempFile("temp", "xml");  
  46.         // 将資料庫内容的DataSet寫入臨時檔案當中  
  47.         FlatXmlDataSet.write(ds, new FileWriter(temp), "UTF-8");  
  48.     }  
  49.     protected void backupCustom(String... tableName) throws Exception {  
  50.         // 此種形式能儲存某幾張表的的資料,而不需要全部備份  
  51.         QueryDataSet qds = new QueryDataSet(connection);  
  52.         for (String str : tableName) {  
  53.             qds.addTable(str);  
  54.         }  
  55.         temp = File.createTempFile("temp", "xml");  
  56.         FlatXmlDataSet.write(qds, new FileWriter(temp), "UTF-8");  
  57.     }  
  58.     protected void recover() throws Exception {  
  59.         // 擷取資料庫内容的臨時檔案生成DataSet  
  60.         IDataSet ds = new FlatXmlDataSet(new FlatXmlProducer(new InputSource(  
  61.                 new FileInputStream(temp))));  
  62.         // 使用DatabaseOperation的枚舉方法先清空資料庫内容再還原資料庫  
  63.         DatabaseOperation.CLEAN_INSERT.execute(connection, ds);  
  64.     }  
  65. }  

      以下為單元測試類 

  1. package com.accentrix.ray;  
  2. import static org.junit.Assert.assertEquals;  
  3. import static org.junit.Assert.assertNotNull;  
  4. import org.dbunit.dataset.IDataSet;  
  5. import org.dbunit.operation.DatabaseOperation;  
  6. import org.junit.Test;  
  7. public class TestUser extends BaseTest {  
  8.     @Test 
  9.     public void testGetUser() throws Exception {  
  10.         // 首先擷取到default-data.xml檔案中的内容  
  11.         IDataSet ds = getDataSet("default-data.xml");  
  12.         // 調用backupAll方法儲存資料庫表  
  13.         backupAll();  
  14.         // 将default-data.xml中的内容替換成資料庫的内容  
  15.         DatabaseOperation.CLEAN_INSERT.execute(connection, ds);  
  16.         // 進行單元測試邏輯的判斷  
  17.         User user = userService.get(1);  
  18.         assertNotNull(user);  
  19.         assertEquals(user.getId(), new Integer(1));  
  20.         assertEquals(user.getPassword(), "456");  
  21.         assertEquals(user.getUsername(), "123");  
  22.         // 最後進行資料庫的恢複  
  23.         // 當然我們也可以将backupAll和recover放在@Before和@After當中  
  24.         recover();  
  25.     }  
  26. }  

總結: 

      使用了DBUnit後可以實作了對資料庫的隔離,成功彌補了JUnit單元測試不清理資料現場的缺憾,實際上DBUnit隻是簡單的在單元測試前把資料庫的資料進行了備份然後插入xml中配置好的資料,在測試結束後再用備份好的原資料庫資料填充回資料庫.但當面對複雜的表關系和大資料量的時候,每次進行測試都進行資料的備份,也是一個很大的負擔,而且把全部的測試資料都編寫在xml當中也是很大的工作量 

繼續閱讀