單元測試系列之4:使用Unitils測試DAO層

Spring 的測試框架爲我們提供一個強大的測試環境,解決日常單元測試中遇到的大部分測試難題:如運行多個測試用例和測試方法時,Spring上下文只需創建一次; 數據庫現場不受破壞;方便手工指定Spring配置文件、手工設定Spring容器是否需要重新加載等。但也存在不足的地方,基本上所有的Java應用都 涉及數據庫,帶數據庫應用系統的測試難點在於數據庫測試數據的準備、維護、驗證及清理。Spring 測試框架並不能很好地解決所有問題。要解決這些問題,必須整合多方資源,如DbUnit、Unitils、Mokito等。其中Unitils正是這樣的 一個測試框架。

數據庫測試的難點

    按照Kent Back的觀點,單元測試最重要的特性之一應該是可重複性。不可重複的單元測試是沒有價值的。因此好的單元測試應該具備獨立性和可重複性,對於業務邏輯 層,可以通過Mockito底層對象和上層對象來獲得這種獨立性和可重複性。而DAO層因爲是和數據庫打交道的層,其單元測試依賴於數據庫中的數據。要實 現DAO層單元測試的可重複性就需要對每次因單元測試引起數據庫中的數據變化進行還原,也就是保護單元測試數據庫的數據現場。

擴展Dbunit用Excel準備數據

    在測試數據訪問層(DAO)時,通常需要經過測試數據的準備、維護、驗證及清理的過程。這個過程不僅煩鎖,而且容易出錯,如數據庫現場容易遭受破壞、如何 對數據操作正確性進行檢查等。雖然Spring測試框架在這一方面爲我們減輕了很多工作,如通過事務回滾機制來保存數據庫現場等,但對測試數據及驗證數據 準備方面還沒有一種很好的處理方式。Unitils框架出現,改變了難測試DAO的局面,它將SpringModule、DatabaseModule、 DbUnitModule等整合在一起,使得DAO的單元測試變得非常容易。基於Unitils框架的DAO測試過程如圖16-6所示。



   以JUnit作爲整個測試的基礎框架,並採用DbUnit作爲自動管理數據庫的工具,以XML、Excel作爲測試數據及驗證數據準備,最後通過 Unitils的數據集註解從Excel、XML文件中加載測試數據。使用一個註解標籤就可以完成加載、刪除數據操作。由於XML作爲數據集易用性不如 Excel,在這裏就不對XML數據集進行講解。下面我們主要講解如何應用Excel作爲準備及驗證數據的載體,減化DAO單元測試。由於Unitils 沒有提供訪問Excel的數據集工廠,因此需要編寫插件支持Excel格式數據源。Unitils提供一個訪問XML的數據集工廠 MultiSchemaXmlDataSetFactory,其繼承自DbUnit提供的數據集工廠接口DataSetFactory。我們可以參考這個 XML數據集工廠類,編寫一個訪問Excel的數據集工廠MultiSchemaXlsDataSetFactory及Excel數據集讀取器 MultiSchemaXlsDataSetReader,然後在數據集讀取器中調用Apache POI類庫來讀寫Excel文件。如代碼清單16-20所示。

Java代碼  收藏代碼
  1. import org.unitils.core.UnitilsException;  
  2. import org.unitils.DbUnit.datasetfactory.DataSetFactory;  
  3. import org.unitils.DbUnit.util.MultiSchemaDataSet;  
  4. …  
  5. public class MultiSchemaXlsDataSetFactory implements DataSetFactory {  
  6. protected String defaultSchemaName;  
  7.   
  8.     //① 初始化數據集工廠  
  9.     public void init(Properties configuration, String defaultSchemaName) {  
  10.         this.defaultSchemaName = defaultSchemaName;  
  11.     }  
  12.   
  13.     //② 從Excel文件創建數據集  
  14.     public MultiSchemaDataSet createDataSet(File... dataSetFiles) {  
  15.         try {  
  16.             MultiSchemaXlsDataSetReader xlsDataSetReader =   
  17.                   new MultiSchemaXlsDataSetReader(defaultSchemaName);  
  18.             return xlsDataSetReader.readDataSetXls(dataSetFiles);  
  19.         } catch (Exception e) {  
  20.             throw new UnitilsException("創建數據集失敗: "  
  21.                     + Arrays.toString(dataSetFiles), e);  
  22.         }  
  23.     }  
  24.   
  25.     //③  獲取數據集文件的擴展名  
  26.     public String getDataSetFileExtension() {  
  27.         return "xls";  
  28.     }  
  29. }  
  30. …  



   與XML數據集工廠MultiSchemaXmlDataSetFactory一樣,Excel的數據集工廠也需要實現數據集工廠接口 DataSetFactory的三個方法:init(…)、createDataSet(File... dataSetFiles)、getDataSetFileExtension()。在①處,初始化數據集工廠,需要設置一個默認的數據庫表模式名稱 defaultSchemaName。在②處,執行創建多數據集,具體讀取構建數據集的過程封裝在Excel讀取器 MultiSchemaXlsDataSetReader中。在③處,獲取數據集文件的擴展名,對Excel文件而言就是“xls”。下面來看一下這個數 據集讀取器的實現代碼。

Java代碼  收藏代碼
  1. import org.unitils.core.UnitilsException;  
  2. import org.unitils.DbUnit.datasetfactory.DataSetFactory;  
  3. import org.unitils.DbUnit.util.MultiSchemaDataSet;  
  4. …  
  5. // Excel數據集讀取器  
  6. public class MultiSchemaXlsDataSetReader {  
  7.     private String defaultSchemaName;  
  8.       
  9.     public MultiSchemaXlsDataSetReader(String defaultSchemaName) {  
  10.         this.defaultSchemaName = defaultSchemaName;  
  11.     }     
  12.      // Excel數據集讀取器  
  13.     public MultiSchemaDataSet readDataSetXls(File... dataSetFiles) {  
  14.         try {  
  15.             Map<String, List<ITable>> tableMap = getTables(dataSetFiles);  
  16.             MultiSchemaDataSet dataSets = new MultiSchemaDataSet();  
  17.             for (Entry<String, List<ITable>> entry : tableMap.entrySet()) {  
  18.                 List<ITable> tables = entry.getValue();  
  19.                 try {  
  20.                     DefaultDataSet ds = new DefaultDataSet(tables  
  21.                             .toArray(new ITable[] {}));  
  22.                     dataSets.setDataSetForSchema(entry.getKey(), ds);  
  23.                 } catch (AmbiguousTableNameException e) {  
  24.                     throw new UnitilsException("構造DataSet失敗!",  e);  
  25.                 }  
  26.             }  
  27.             return dataSets;  
  28.         } catch (Exception e) {  
  29.             throw new UnitilsException("解析EXCEL文件出錯:", e);  
  30.         }  
  31.     }  
  32. …  
  33. }  
  34. …  



根據傳入的多個Excel文件,構造一個多數據集。 其中一個數據集對應一個Excel文件,一個Excel的Sheet表對應一個數據庫Table。通過DbUnit提供Excel數據集構造類 XlsDataSet,可以很容易將一個Excel文件轉換爲一個數據集:XlsDataSet(new FileInputStream(xlsFile))。最後將得到的多個DataSet用MultiSchemaDataSet進行封裝。
下面就以一個用戶DAO的實現類WithoutSpringUserDaoImpl爲例,介紹如何使用我們實現的Excel數據集工廠。爲了讓Unitils使用自定義的數據集工廠,需要在unitils.properties配置文件中指定自定義的數據集工廠。

引用

DbUnitModule.DataSet.factory.default=sample.unitils.dataset.excel.MultiSchemaXlsDataSetFactory
DbUnitModule.ExpectedDataSet.factory.default=sample.unitils.dataset.excel.MultiSchemaXlsDataSetFactory



其中DbUnitModule.DataSet.factory.default是配置數據集工廠類,在測試方法中可以使用@DataSet 註解加載指定的準備數據。默認是XML數據集工廠,這裏指定自定義數據集工廠類全限定名爲   sample.unitils.dataset.excel.MultiSchemaXlsDataSetFactory。
其中DbUnitModule. ExpectedDataSet.factory.default是配置驗證數據集工廠類,也是指定自定義數據集工廠類,使用@ ExpectedDataSet註解加載驗證數據。

Java代碼  收藏代碼
  1. import org.unitils.core.UnitilsException;  
  2. import org.unitils.DbUnit.datasetfactory.DataSetFactory;  
  3. import org.unitils.DbUnit.util.MultiSchemaDataSet;  
  4. …  
  5. public class UserDaoTest extends UnitilsJUnit4 {  
  6.     @Test  
  7.     @DataSet //① 準備測試數據  
  8.     public void getUser() {  
  9.         …  
  10.     }  
  11.   
  12.     @Test  
  13.     @DataSet("BaobaoTao.SaveUser.xls"//② 準備測試數據 -  
  14.     @ExpectedDataSet //③ 準備驗證數據  
  15.     public void saveUser()throws Exception  {  
  16.         …  
  17.     }  
  18.   
  19. }  
  20. …  



@DateSet 註解表示了測試時需要尋找DbUnit的數據集文件進行加載,如果沒有指明數據集的文件名,則Unitils自動在當前測試用例所在類路徑下加載文件名爲 測試用例類名的數據集文件,實例中①處,將到UserDaoTest.class所在目錄加載WithExcelUserDaoTest.xls 數據集文件。
@ExpectedDataSet註解用於加載驗證數據集文件,如果沒有指明數據集的文件名,則會在當前測試用例所在類路徑下加載文件名爲 testClassName.methodName-result.xls的數據集文件。實例中③處將加載UserDaoTest. saveUser.result.xls數據集文件。

測試實戰

使用JUnit作爲基礎測試框架,結合Unitils、DbUnit管理測試數據,並使用我們編寫的Excel數據集工廠(見代碼清單16 20)。從Excel數據集文件中獲取準備數據及驗證數據,並使用HSQLDB作爲測試數據庫。下面詳細介紹如何應用Excel準備數據集及驗證數據集來 測試DAO。
在進行DAO層的測試之前,我們先來認識一下需要測試的UserDaoImpl用戶數據訪問類。UserDaoImpl用戶數據訪問類中擁有一個獲取用戶信息和保存註冊用戶信息的方法,其代碼如下所示。

Java代碼  收藏代碼
  1. import java.util.List;  
  2. import org.hibernate.Session;  
  3. import org.hibernate.SessionFactory;  
  4. import org.springframework.orm.hibernate3.HibernateTemplate;  
  5. import com.baobaotao.dao.UserDao;  
  6. import com.baobaotao.domain.User;  
  7. public class UserDaoImpl implements UserDao {  
  8.   
  9.     //通過用戶名獲取用戶信息  
  10. public User findUserByUserName(String userName) {  
  11.         String hql = " from User u where u.userName=?";  
  12.         List<User> users = getHibernateTemplate().find(hql, userName);  
  13.         if (users != null && users.size() > 0)  
  14.             return users.get(0);  
  15.         else  
  16.             return null;  
  17.     }  
  18.   
  19. //保存用戶信息  
  20.     public void save(User user) {  
  21.         getHibernateTemplate().saveOrUpdate(user);  
  22.     }  
  23.     …  
  24. }  



我們認識了需要測試的UserDaoImpl用戶數據訪問類之後,還需要認識一下用於表示用戶領域的對象User,在演示測試保存用戶信息及獲取用戶信息時需要用到此領域對象,其代碼如下所示。

Java代碼  收藏代碼
  1. import javax.persistence.Column;  
  2. import javax.persistence.Entity;  
  3. …  
  4. @Entity  
  5. @GeneratedValue(strategy = GenerationType.IDENTITY)  
  6. @Table(name = "t_user")  
  7. public class User implements Serializable{  
  8.     @Id  
  9.     @Column(name = "user_id")  
  10.     protected int userId;  
  11.   
  12.     @Column(name = "user_name")  
  13.     protected String userName;  
  14.   
  15.     protected String password;  
  16.   
  17.     @Column(name = "last_visit")  
  18.     protected Date lastVisit;  
  19.   
  20.     @Column(name = "last_ip")  
  21.     protected String lastIp;  
  22.   
  23.     @Column(name = "credits")  
  24.     private int credits;  
  25.     …  
  26. }  


用戶登錄日誌領域對象LoginLog與用戶領域對象Hibernate註解配置一致,這裏就不再列出,讀者可以參考本書附帶光盤中的實例代 碼。在實例測試中,我們直接使用Hibernate進行持久化操作,所以還需要對Hibernate進行相應配置,詳細的配置清單如下所示。

Xml代碼  收藏代碼
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <!DOCTYPE hibernate-configuration PUBLIC  
  3. "-//Hibernate/Hibernate Configuration DTD 3.0//EN"  
  4. "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">  
  5. <hibernate-configuration>  
  6.     <session-factory>  
  7.         <!--①  SQL方言,這邊設定的是HSQL -->  
  8.         <property name="dialect">org.hibernate.dialect.HSQLDialect</property>  
  9.         <!--② 數據庫連接配置 -->  
  10.         <property name="hibernate.connection.driver_class">org.hsqldb.jdbcDriver</property>  
  11.         <property name="hibernate.connection.url">  
  12. jdbc:hsqldb:data/sampledb  
  13. </property>  
  14.         <!--設置連接數據庫的用戶名-->  
  15.         <property name="hibernate.connection.username">sa</property>  
  16.         <!--設置連接數據庫的密碼-->  
  17.         <property name="hibernate.connection.password"></property>  
  18.         <!--③ 設置顯示sql語句方便調試-->  
  19.         <property name="hibernate.show_sql">true</property>  
  20.         <!--④  配置映射 -->  
  21.         <property name="configurationClass">  
  22. org.hibernate.cfg.AnnotationConfiguration  
  23. </property>  
  24.         <mapping class="com.baobaotao.domain.User" />  
  25.         <mapping class="com.baobaotao.domain.LoginLog" />  
  26.     </session-factory>  
  27. </hibernate-configuration>  



  選用HSQLDB作爲測試數據庫,在①處,配置HSQLDB的SQL方言HSQLDialect。在②處,對連接數據庫驅動及數據庫連接進行相應的配置。 爲了方便測試調試,在③處設置顯示Hibernate生成的SQL語句。在④處啓用Hibernate的註解功能,並配置相應的領域對象,如實例中的 User、LoginLog。將配置好的hibernate.cfg.xml放在src目錄下。

配置Unitils測試環境

  要在單元測試中更好地使用Unitils ,首先需要在測試源碼的根目錄中創建一個項目級unitils.properties 配置文件,實例中unitils.properties詳細配置清單如下所示。

Java代碼  收藏代碼
  1. #① 啓用unitils所需模塊   
  2. unitils.modules=database,dbunit,hibernate,spring  
  3.   
  4. #自定義擴展模塊,詳見實例源碼  
  5. unitils.module.dbunit.className=sample.unitils.module.CustomExtModule  
  6.   
  7. #② 配置數據庫連接  
  8. database.driverClassName=org.hsqldb.jdbcDriver  
  9. database.url=jdbc:hsqldb:data/sampledb;shutdown=true  
  10. database.userName=sa  
  11. database.password=  
  12. database.schemaNames=public  
  13. database.dialect = hsqldb  
  14.   
  15. #③ 配置數據庫維護策略.  
  16. updateDataBaseSchema.enabled=true  
  17.   
  18. #④ 配置數據庫表創建策略  
  19. dbMaintainer.autoCreateExecutedScriptsTable=true  
  20. dbMaintainer.script.locations=D:/masterSpring/chapter16/resources/dbscripts  
  21.   
  22. #⑤ 數據集加載策略  
  23. #DbUnitModule.DataSet.loadStrategy.default=org.unitils.dbunit.datasetloadstrategy.InsertLoadStrategy   
  24.   
  25.   
  26. #⑥ 配置數據集工廠  
  27. DbUnitModule.DataSet.factory.default=sample.unitils.dataset.excel.MultiSchemaXlsDataSetFactory  
  28. DbUnitModule.ExpectedDataSet.factory.default=sample.unitils.dataset.excel.MultiSchemaXlsDataSetFactory  
  29.   
  30. #⑦ 配置事務策略  
  31. DatabaseModule.Transactional.value.default=commit  
  32.   
  33. #⑧ 配置數據集結構模式XSD生成路徑  
  34. dataSetStructureGenerator.xsd.dirName=resources/xsd  



   我們知道unitils.properties中配置的屬性是整個項目級別的,整個項目都可以使用這些全局的屬性配置。特定用戶使用的屬性可以設置在 unitils-local.properties 文件中,比如user、password和schema,這樣每個開發者就使用自定義的測試數據庫的schema,而且彼此之間也不會產生影響,實例的詳 細配置清單如下所示。

Java代碼  收藏代碼
  1. …  
  2. database.userName=sa  
  3. database.password=  
  4. database.schemaNames=public  
  5. …  


  
   如果用戶分別在unitils.properties文件及unitils -local.properties文件中對相同屬性配置不同值時,將會以unitils-local.properties 配置內容爲主。如在unitils.properties配置文件中,也配置了database.schemaNames=xxx,測試時啓用的是用戶自 定義配置中的值database.schemaNames=public。

   默認的數據集加載機制採用先清理後插入的策略,也就是數據在被寫入數據庫的時候是會先刪除數據集中有對應表的數據,然後將數據集中的數據寫入數據庫。這個 加載策略是可配置的,我們可以通過修改DbUnitModule.DataSet.loadStrategy.default的屬性值來改變加載策略。如 實例代碼清單16 27中⑤配置策略,這時加載策略就由先清理後插入變成了插入,數據已經存在表中將不會被刪除,測試數據只是進行插入操作。可選的加載策略列表如下所示。

  •   CleanInsertLoadStrategy:先刪除dateSet中有關表的數據,然後再插入數據。
  •   InsertLoadStrategy:只插入數據。
  •   RefreshLoadStrategy:有同樣key的數據更新,沒有的插入。
  •   UpdateLoadStrategy: 有同樣key的數據更新,沒有的不做任何操作。



配置事務策略

   在測試DAO的時候都會填寫一些測試數據,每個測試運行都會修改或者更新了數據,當下一個測試運行的時候,都需要將數據恢復到原有狀態。如果使用的是 Hibernate或者JPA,需要每個測試都運行在事務中,保證系統的正常工作。默認情況下,事務管理是disabled的,我們可以通過修改 DatabaseModule.Transactional.value.default配置選項,如實例代碼清單16 27中⑧配置策略,這時每個測試都將執行commit,其他可選的配置屬性值有rollback和disabled。

準備測試數據庫及測試數據

   配置好了Unitils基本配置、加載模塊、數據集創建策略、事務策略之後,我們就着手開始測試數據庫及測試數據準備工作,首先我們創建測試數據庫。

創建測試數據庫

   在源碼包根目錄下創建一個dbscripts文件夾(文件夾目錄結構如圖16-7所示),且這個文件夾必須與在unitils.properties 文件中dbMaintainer.script.locations配置項指定的位置一致,如代碼清單16 27中④ 所示。

   在這個文件夾中創建一個數據庫創建腳本文件001_create_sampledb.sql,裏面包含創建用戶表t_user 及登錄日誌表t_login_log,詳細的腳本如下所示。

Sql代碼  收藏代碼
  1. CREATE TABLE t_user (  
  2. user_id INT generated by default as identity (start with 100),  
  3. user_name VARCHAR(30),credits INT,  
  4. password  VARCHAR(32),last_visit timestamp,  
  5. last_ip  VARCHAR(23), primary key (user_id));  
  6.   
  7. CREATE TABLE t_login_log (  
  8. login_log_id  INT generated by default as identity (start with 1),   
  9. user_id   INT,  
  10. ip  VARCHAR(23),  
  11. login_datetime timestamp,  
  12. primary key (login_log_id));  


   細心的讀者可能會發現這個數據庫創建腳本文件名好像存在一定的規則,是的,這個腳本文件命名需要按以下規則命名:版本號 +  “_” +  “自定義名稱” + “ .sql” 。

連接到測試數據庫

   測試DAO時,讀者要有個疑問,測試數據庫用到的數據源來自哪裏,怎麼讓我們測試的DAO類來使用我們的數據源。執行測試實例的時候,Unitils 會根據我們定義的數據庫連接屬性來創建一個數據源實例連接到測試數據庫。隨後的DAO測試會重用相同的數據源實例。建立連接的細節定義在 unitils.properties配置文件中,如代碼清單16 27中的② 配置部分所示。

用Excel準備測試數據

   準備好測試數據庫之後,剩下的工作就是用Excel來準備測試數據及驗證數據,回顧一下我們要測試的UserDaoImpl 類(代碼清單16 24),需要對其中的獲取用戶信息方法findUserByUserName()及保存用戶信息方法saveUser()進行測試,所以我們至少需要準備 三個Excel數據集文件 ,分別是供查詢用戶用的數據集BaobaoTao.Users.xls、供保存用戶信息用的數據集BaobaoTao.SaveUser.xls及供保存 用戶信息用的驗證數據集BaobaoTao. ExpectedSaveUser.xls。下面以用戶數據集BaobaoTao.Users.xls實例進行說明,如圖16-8所示。


   在①處t_user表示數據庫對應的表名稱。在②處表示數據庫中t_user表對應的字段名稱。在③處表示準備測試的模擬數據。一個數據集文件可以對應多 張表,一個Sheet對就一張表。把創建好的數據集文件放到與測試類相同的目錄中,如實例中的UserDaoTest類位於 com.baobaotao.dao包中,則數據集文件需要放到當前包中。其他兩個數據集文件數據結構如圖16-9和16-10所示。



編寫UserDaoImpl的測試用例

  完成了Unitils環境配置、準備測試數據庫及測試數據之後,就可以開始編寫用戶DAO單元測試類,下面我們爲用戶數據訪問UserDaoImpl編寫測試用例類。

Java代碼  收藏代碼
  1. import org.unitils.core.UnitilsException;  
  2. import org.unitils.DbUnit.datasetfactory.DataSetFactory;  
  3. import org.unitils.DbUnit.util.MultiSchemaDataSet;  
  4. …  
  5. @SpringApplicationContext( {"baobaotao-dao.xml" }) //① 初始化Spring容器  
  6. public class UserDaoTest extends UnitilsJUnit4 {  
  7.   
  8.     @SpringBean("jdbcUserDao")  //② 從Spring容器中加載DAO  
  9.     private UserDao userDao;      
  10.   
  11.     @Before  
  12.     public void init() {  
  13.           
  14.     }  
  15.    …  
  16. }  


   在①處,通過Unitils提供@ SpringApplicationContext註解加載Spring配置文件,並初始化Spring容器。在②處,通過@SpringBean註解從 Spring容器加載一個用戶DAO實例。編寫UserDaoTest測試基礎模型之後,接下來就編寫查詢用戶信息 findUserByUserName()的測試方法。

代碼清單16 31 UserDaoTest.findUserByUserName()測試

Java代碼  收藏代碼
  1. import org.unitils.core.UnitilsException;  
  2. import org.unitils.DbUnit.datasetfactory.DataSetFactory;  
  3. import org.unitils.DbUnit.util.MultiSchemaDataSet;  
  4. …  
  5. public class UserDaoTest extends UnitilsJUnit4 {  
  6. …  
  7.   
  8. @Test //① 標誌爲測試方法  
  9.     @DataSet("BaobaoTao.Users.xls"//② 加載準備用戶測試數據  
  10.     public void findUserByUserName() {  
  11.         User user = userDao.findUserByUserName("tony"); //③ 從數據庫中加載tony用戶  
  12.         assertNull("不存在用戶名爲tony的用戶!", user);  
  13.         user = userDao.findUserByUserName("jan"); //④ 從數據庫中加載jan用戶  
  14.         assertNotNull("jan用戶存在!", user);  
  15.         assertEquals("jan", user.getUserName());  
  16.         assertEquals("123456",user.getPassword());  
  17.         assertEquals(10,user.getCredits());   
  18.     }  
  19. …  
  20. }  



   在①處,通過JUnit提供@Test註解,把當前方法標誌爲可測試方法。在②處,通過Unitils提供的@DataSet註解從當前測試類 UserDaoTest.class所在的目錄尋找支持DbUnit的數據集文件並進行加載。執行測試邏輯之前,會把加載的數據集先持久化到測試數據庫 中,具體加載數據集的策略詳見上文“配置數據集加載策略”部分。實例中採用的默認加載策略,即先刪除測試數據庫對應表的數據再插入數據集中的測試數據。這 種策略可以避免不同測試方法加載數據集相互干擾。在③處執行查詢用戶方法時,測試數據庫中t_user表數據已經是如圖16-8 BaobaoTao.Users.xls所示的數據,因此查詢不到“tony”用戶信息。在④處,執行查詢“jan”用戶信息,從測試數據集可以看出,可 以加載到“jan”的詳細信息。最後在IDE中執行UserDaoTest. findUserByUserName()測試方法,按我們預期通過測試,測試結果如圖16-11所示。


   完成了查詢用戶的測試之後,我們開始着手編寫保存用戶信息的測試方法,詳細的實現代碼如下所示。

Java代碼  收藏代碼
  1. import org.unitils.core.UnitilsException;  
  2. import org.unitils.DbUnit.datasetfactory.DataSetFactory;  
  3. import org.unitils.DbUnit.util.MultiSchemaDataSet;  
  4. …  
  5. public class UserDaoTest extends UnitilsJUnit4 {  
  6. …     
  7.   
  8. @Test  //① 標誌爲測試方法  
  9.     @ExpectedDataSet("BaobaoTao.ExpectedSaveUser.xls"//準備驗證數據  
  10.     public void saveUser()throws Exception  {  
  11.         User u = new User();  
  12.         u.setUserId(1);  
  13.         u.setUserName("tom");  
  14.         u.setPassword("123456");  
  15.         u.setLastVisit(getDate("2011-06-06 08:00:00","yyyy-MM-dd HH:mm:ss"));  
  16.         u.setCredits(30);  
  17.         u.setLastIp("127.0.0.1");  
  18.         userDao.save(u);  //執行用戶信息更新操作  
  19.     }  
  20. …  
  21. }  


   在①處,通過JUnit提供@Test註解,把當前方法標誌爲可測試方法。在②處,通過Unitils提供的@ExpectedDataSet註解從當前 測試類UserDaoTest.class所在的目錄尋找支持DbUnit的驗證數據集文件並進行加載,之後驗證數據集裏的數據和數據庫中的數據是否一 致。在UserDaoTest.saveUser()測試方法中創建一個User實例,並設置與圖16-10 驗證數據集中相同的數據,然後執行保存用戶操作。最後在IDE中執行UserDaoTest.saveUser()測試方法,執行結果如圖16-12所 示。

   雖然已經成功完成了保存用戶信息UserDaoTest.saveUser() 方法測試,但還是存在不足的地方,我們測試數據通過硬編碼方式直接設置在User實例中。如果需要更改測試數據,只能更改測試代碼。大大削減了測試的靈活 性。如果能直接從Excel數據集獲取測試數據,並自動綁定到目標對象,那我們的測試用例就更加完美。爲此筆者編寫了一個獲取Excel數據集Bean工 廠XlsDataSetBeanFactory,用於自動綁定數據集到測試對象。我們對上面的測試方法進行整改,實現代碼如代碼清單16-33所示。

Java代碼  收藏代碼
  1. import org.unitils.core.UnitilsException;  
  2. import org.unitils.DbUnit.datasetfactory.DataSetFactory;  
  3. import org.unitils.DbUnit.util.MultiSchemaDataSet;  
  4. import sample.unitils.dataset.util.XlsDataSetBeanFactory;  
  5. …  
  6. public class UserDaoTest extends UnitilsJUnit4 {  
  7. …     
  8.   
  9. @Test  //① 標誌爲測試方法  
  10.     @ExpectedDataSet("BaobaoTao.ExpectedSaveUser.xls"//準備驗證數據  
  11.     public void saveUser()throws Exception  {  
  12.   
  13. //② 從保存數據集中創建Bean  
  14.     User u  = XlsDataSetBeanFactory.createBean("BaobaoTao.SaveUser.xls”   
  15. ,"t_user", User.class);  
  16.         userDao.save(u); //③ 執行用戶信息更新操作  
  17.     }  
  18. …  
  19. }  


   在②處,通過XlsDataSetBeanFactory.createBean()方法,從當前測試類所在目錄加載 BaobaoTao.SaveUser.xls數據集文件,其數據結構如圖16-9所示。把BaobaoTao.SaveUser.xls中名稱爲 t_user 的Sheet頁中的數據綁定到User對象,如果當前Sheet頁有多條記錄,可以通過 XlsDataSetBeanFactory.createBeans()獲取用戶列表List<User>。最後在IDE中重新執行 UserDaoTest.saveUser()測試方法,執行結果如圖16-13所示。

   從測試結果可以看出,執行UserDaoTest.saveUser()測試失敗。從右邊的失敗報告信息我們可以看出,是由於模擬用戶的積分與我們期望數 據不一致造成,期望用戶積分是30,而我們保存用戶的積分是10。重新對比一下圖16-9 BaobaoTao.SaveUser.xls數據集數據與圖16-10 BaobaoTao.ExpectedSaveUser.xls數據集的數據,確實我們準備保存數據集的數據與驗證結果的數據不一致。把 BaobaoTao.SaveUser.xls數據集中的用戶積分更改爲30,最後在IDE中重新執行UserDaoTest.saveUser()測試 方法,執行結果如圖16-14所示。

   從測試結果可以看出,保存用戶通過測試。從上述的測試實戰,我們已經體驗到用Excel準備測試數據與驗證數據帶來的便捷性。到此,我們完成了DAO測試 的整個過程,對於XlsDataSetBeanFactory具體實現,讀者可以查看本章的實例源碼,這裏就不做詳細分析。下面是實現基本骨架。

Java代碼  收藏代碼
  1. import org.dbunit.dataset.Column;  
  2. import org.dbunit.dataset.DataSetException;  
  3. import org.dbunit.dataset.IDataSet;  
  4. import org.dbunit.dataset.ITable;  
  5. import org.dbunit.dataset.excel.XlsDataSet;  
  6. …     
  7. public class XlsDataSetBeanFactory {  
  8.       
  9.     //從Excel數據集文件創建多個Bean  
  10.     public static <T> List<T> createBeans(String file, String tableName,  
  11.             Class<T> clazz) throws Exception {  
  12.         BeanUtilsBean beanUtils = createBeanUtils();  
  13.         List<Map<String, Object>> propsList = createProps(file, tableName);  
  14.         List<T> beans = new ArrayList<T>();  
  15.         for (Map<String, Object> props : propsList) {  
  16.             T bean = clazz.newInstance();  
  17.             beanUtils.populate(bean, props);  
  18.             beans.add(bean);  
  19.         }  
  20.         return beans;  
  21.     }  
  22.   
  23.     //從Excel數據集文件創建多個Bean  
  24.     public static <T> T createBean(String file, String tableName, Class<T> clazz)  
  25.             throws Exception {  
  26.         BeanUtilsBean beanUtils = createBeanUtils();  
  27.         List<Map<String, Object>> propsList = createProps(file, tableName);  
  28.         T bean = clazz.newInstance();  
  29.         beanUtils.populate(bean, propsList.get(0));  
  30.         return bean;  
  31.     }  
  32. …  

http://stamen.iteye.com/blog/1484589

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章