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

[b][size=x-large]資料庫測試的難點[/size][/b]

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

[b][size=x-large]擴充套件Dbunit用Excel準備資料[/size][/b]

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

[img]http://dl.iteye.com/upload/attachment/0066/4676/eb64e901-9761-3b26-8e68-bc74a0a8aef0.jpg[/img]

以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所示。

import org.unitils.core.UnitilsException;
import org.unitils.DbUnit.datasetfactory.DataSetFactory;
import org.unitils.DbUnit.util.MultiSchemaDataSet;

public class MultiSchemaXlsDataSetFactory implements DataSetFactory {
protected String defaultSchemaName;

//① 初始化資料集工廠
public void init(Properties configuration, String defaultSchemaName) {
this.defaultSchemaName = defaultSchemaName;
}

//② 從Excel檔案建立資料集
public MultiSchemaDataSet createDataSet(File... dataSetFiles) {
try {
MultiSchemaXlsDataSetReader xlsDataSetReader =
new MultiSchemaXlsDataSetReader(defaultSchemaName);
return xlsDataSetReader.readDataSetXls(dataSetFiles);
} catch (Exception e) {
throw new UnitilsException("建立資料集失敗: "
+ Arrays.toString(dataSetFiles), e);
}
}

//③ 獲取資料集檔案的副檔名
public String getDataSetFileExtension() {
return "xls";
}
}



與XML資料集工廠MultiSchemaXmlDataSetFactory一樣,Excel的資料集工廠也需要實現資料集工廠介面DataSetFactory的三個方法:init(…)、createDataSet(File... dataSetFiles)、getDataSetFileExtension()。在①處,初始化資料集工廠,需要設定一個預設的資料庫表模式名稱defaultSchemaName。在②處,執行建立多資料集,具體讀取構建資料集的過程封裝在Excel讀取器MultiSchemaXlsDataSetReader中。在③處,獲取資料集檔案的副檔名,對Excel檔案而言就是“xls”。下面來看一下這個資料集讀取器的實現程式碼。

import org.unitils.core.UnitilsException;
import org.unitils.DbUnit.datasetfactory.DataSetFactory;
import org.unitils.DbUnit.util.MultiSchemaDataSet;

// Excel資料集讀取器
public class MultiSchemaXlsDataSetReader {
private String defaultSchemaName;

public MultiSchemaXlsDataSetReader(String defaultSchemaName) {
this.defaultSchemaName = defaultSchemaName;
}
// Excel資料集讀取器
public MultiSchemaDataSet readDataSetXls(File... dataSetFiles) {
try {
Map<String, List<ITable>> tableMap = getTables(dataSetFiles);
MultiSchemaDataSet dataSets = new MultiSchemaDataSet();
for (Entry<String, List<ITable>> entry : tableMap.entrySet()) {
List<ITable> tables = entry.getValue();
try {
DefaultDataSet ds = new DefaultDataSet(tables
.toArray(new ITable[] {}));
dataSets.setDataSetForSchema(entry.getKey(), ds);
} catch (AmbiguousTableNameException e) {
throw new UnitilsException("構造DataSet失敗!", e);
}
}
return dataSets;
} catch (Exception e) {
throw new UnitilsException("解析EXCEL檔案出錯:", e);
}
}

}



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

[quote]…
DbUnitModule.DataSet.factory.default=sample.unitils.dataset.excel.MultiSchemaXlsDataSetFactory
DbUnitModule.ExpectedDataSet.factory.default=sample.unitils.dataset.excel.MultiSchemaXlsDataSetFactory
[/quote]

  其中DbUnitModule.DataSet.factory.default是配置資料集工廠類,在測試方法中可以使用@DataSet註解載入指定的準備資料。預設是XML資料集工廠,這裡指定自定義資料集工廠類全限定名為  sample.unitils.dataset.excel.MultiSchemaXlsDataSetFactory。
  其中DbUnitModule. ExpectedDataSet.factory.default是配置驗證資料集工廠類,也是指定自定義資料集工廠類,使用@ ExpectedDataSet註解載入驗證資料。

import org.unitils.core.UnitilsException;
import org.unitils.DbUnit.datasetfactory.DataSetFactory;
import org.unitils.DbUnit.util.MultiSchemaDataSet;

public class UserDaoTest extends UnitilsJUnit4 {
@Test
@DataSet //① 準備測試資料
public void getUser() {

}

@Test
@DataSet("BaobaoTao.SaveUser.xls") //② 準備測試資料 -
@ExpectedDataSet //③ 準備驗證資料
public void saveUser()throws Exception {

}

}



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

[b][size=x-large]測試實戰[/size][/b]

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

import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.orm.hibernate3.HibernateTemplate;
import com.baobaotao.dao.UserDao;
import com.baobaotao.domain.User;
public class UserDaoImpl implements UserDao {

//通過使用者名稱獲取使用者資訊
public User findUserByUserName(String userName) {
String hql = " from User u where u.userName=?";
List<User> users = getHibernateTemplate().find(hql, userName);
if (users != null && users.size() > 0)
return users.get(0);
else
return null;
}

//儲存使用者資訊
public void save(User user) {
getHibernateTemplate().saveOrUpdate(user);
}

}


  我們認識了需要測試的UserDaoImpl使用者資料訪問類之後,還需要認識一下用於表示使用者領域的物件User,在演示測試儲存使用者資訊及獲取使用者資訊時需要用到此領域物件,其程式碼如下所示。

import javax.persistence.Column;
import javax.persistence.Entity;

@Entity
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Table(name = "t_user")
public class User implements Serializable{
@Id
@Column(name = "user_id")
protected int userId;

@Column(name = "user_name")
protected String userName;

protected String password;

@Column(name = "last_visit")
protected Date lastVisit;

@Column(name = "last_ip")
protected String lastIp;

@Column(name = "credits")
private int credits;

}

  使用者登入日誌領域物件LoginLog與使用者領域物件Hibernate註解配置一致,這裡就不再列出,讀者可以參考本書附帶光碟中的例項程式碼。在例項測試中,我們直接使用Hibernate進行持久化操作,所以還需要對Hibernate進行相應配置,詳細的配置清單如下所示。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!--① SQL方言,這邊設定的是HSQL -->
<property name="dialect">org.hibernate.dialect.HSQLDialect</property>
<!--② 資料庫連線配置 -->
<property name="hibernate.connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="hibernate.connection.url">
jdbc:hsqldb:data/sampledb
</property>
<!--設定連線資料庫的使用者名稱-->
<property name="hibernate.connection.username">sa</property>
<!--設定連線資料庫的密碼-->
<property name="hibernate.connection.password"></property>
<!--③ 設定顯示sql語句方便除錯-->
<property name="hibernate.show_sql">true</property>
<!--④ 配置對映 -->
<property name="configurationClass">
org.hibernate.cfg.AnnotationConfiguration
</property>
<mapping class="com.baobaotao.domain.User" />
<mapping class="com.baobaotao.domain.LoginLog" />
</session-factory>
</hibernate-configuration>


選用HSQLDB作為測試資料庫,在①處,配置HSQLDB的SQL方言HSQLDialect。在②處,對連線資料庫驅動及資料庫連線進行相應的配置。為了方便測試除錯,在③處設定顯示Hibernate生成的SQL語句。在④處啟用Hibernate的註解功能,並配置相應的領域物件,如例項中的User、LoginLog。將配置好的hibernate.cfg.xml放在src目錄下。

[b][size=large]配置Unitils測試環境[/size][/b]

要在單元測試中更好地使用Unitils ,首先需要在測試原始碼的根目錄中建立一個專案級unitils.properties 配置檔案,例項中unitils.properties詳細配置清單如下所示。
#① 啟用unitils所需模組 
unitils.modules=database,dbunit,hibernate,spring

#自定義擴充套件模組,詳見例項原始碼
unitils.module.dbunit.className=sample.unitils.module.CustomExtModule

#② 配置資料庫連線
database.driverClassName=org.hsqldb.jdbcDriver
database.url=jdbc:hsqldb:data/sampledb;shutdown=true
database.userName=sa
database.password=
database.schemaNames=public
database.dialect = hsqldb

#③ 配置資料庫維護策略.
updateDataBaseSchema.enabled=true

#④ 配置資料庫表建立策略
dbMaintainer.autoCreateExecutedScriptsTable=true
dbMaintainer.script.locations=D:/masterSpring/chapter16/resources/dbscripts

#⑤ 資料集載入策略
#DbUnitModule.DataSet.loadStrategy.default=org.unitils.dbunit.datasetloadstrategy.InsertLoadStrategy


#⑥ 配置資料集工廠
DbUnitModule.DataSet.factory.default=sample.unitils.dataset.excel.MultiSchemaXlsDataSetFactory
DbUnitModule.ExpectedDataSet.factory.default=sample.unitils.dataset.excel.MultiSchemaXlsDataSetFactory

#⑦ 配置事務策略
DatabaseModule.Transactional.value.default=commit

#⑧ 配置資料集結構模式XSD生成路徑
dataSetStructureGenerator.xsd.dirName=resources/xsd


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


database.userName=sa
database.password=
database.schemaNames=public



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

預設的資料集載入機制採用先清理後插入的策略,也就是資料在被寫入資料庫的時候是會先刪除資料集中有對應表的資料,然後將資料集中的資料寫入資料庫。這個載入策略是可配置的,我們可以通過修改DbUnitModule.DataSet.loadStrategy.default的屬性值來改變載入策略。如例項程式碼清單16 27中⑤配置策略,這時載入策略就由先清理後插入變成了插入,資料已經存在表中將不會被刪除,測試資料只是進行插入操作。可選的載入策略列表如下所示。
[list]
[*] CleanInsertLoadStrategy:先刪除dateSet中有關表的資料,然後再插入資料。
[*] InsertLoadStrategy:只插入資料。
[*] RefreshLoadStrategy:有同樣key的資料更新,沒有的插入。
[*] UpdateLoadStrategy: 有同樣key的資料更新,沒有的不做任何操作。
[/list]

[b][size=large]配置事務策略[/size][/b]

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

[b][size=large]準備測試資料庫及測試資料[/size][/b]

配置好了Unitils基本配置、載入模組、資料集建立策略、事務策略之後,我們就著手開始測試資料庫及測試資料準備工作,首先我們建立測試資料庫。

[b][size=large]建立測試資料庫[/size][/b]

在原始碼包根目錄下建立一個dbscripts資料夾(資料夾目錄結構如圖16-7所示),且這個資料夾必須與在unitils.properties 檔案中dbMaintainer.script.locations配置項指定的位置一致,如程式碼清單16 27中④ 所示。
[img]http://dl.iteye.com/upload/attachment/0066/4679/a8dd908b-b1f7-3156-a1a7-126e60f2188b.jpg[/img]
在這個資料夾中建立一個數據庫建立指令碼檔案001_create_sampledb.sql,裡面包含建立使用者表t_user 及登入日誌表t_login_log,詳細的指令碼如下所示。
CREATE TABLE t_user (
user_id INT generated by default as identity (start with 100),
user_name VARCHAR(30),credits INT,
password VARCHAR(32),last_visit timestamp,
last_ip VARCHAR(23), primary key (user_id));

CREATE TABLE t_login_log (
login_log_id INT generated by default as identity (start with 1),
user_id INT,
ip VARCHAR(23),
login_datetime timestamp,
primary key (login_log_id));

細心的讀者可能會發現這個資料庫建立指令碼檔名好像存在一定的規則,是的,這個指令碼檔案命名需要按以下規則命名:版本號 + “_” + “自定義名稱” + “ .sql” 。

[b][size=large]連線到測試資料庫[/size][/b]

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

[b][size=large]用Excel準備測試資料[/size][/b]

準備好測試資料庫之後,剩下的工作就是用Excel來準備測試資料及驗證資料,回顧一下我們要測試的UserDaoImpl 類(程式碼清單16 24),需要對其中的獲取使用者資訊方法findUserByUserName()及儲存使用者資訊方法saveUser()進行測試,所以我們至少需要準備三個Excel資料集檔案 ,分別是供查詢使用者用的資料集BaobaoTao.Users.xls、供儲存使用者資訊用的資料集BaobaoTao.SaveUser.xls及供儲存使用者資訊用的驗證資料集BaobaoTao. ExpectedSaveUser.xls。下面以使用者資料集BaobaoTao.Users.xls例項進行說明,如圖16-8所示。

[img]http://dl.iteye.com/upload/attachment/0066/4683/ba717c58-abfb-31cf-8e8c-ee203b904ab1.jpg[/img]
在①處t_user表示資料庫對應的表名稱。在②處表示資料庫中t_user表對應的欄位名稱。在③處表示準備測試的模擬資料。一個數據集檔案可以對應多張表,一個Sheet對就一張表。把建立好的資料集檔案放到與測試類相同的目錄中,如例項中的UserDaoTest類位於com.baobaotao.dao包中,則資料集檔案需要放到當前包中。其他兩個資料集檔案資料結構如圖16-9和16-10所示。

[img]http://dl.iteye.com/upload/attachment/0066/4685/f2030fe5-6ac4-38af-aae9-c3a9b4f91482.jpg[/img]

[b][size=large]編寫UserDaoImpl的測試用例[/size][/b]

完成了Unitils環境配置、準備測試資料庫及測試資料之後,就可以開始編寫使用者DAO單元測試類,下面我們為使用者資料訪問UserDaoImpl編寫測試用例類。
import org.unitils.core.UnitilsException;
import org.unitils.DbUnit.datasetfactory.DataSetFactory;
import org.unitils.DbUnit.util.MultiSchemaDataSet;

@SpringApplicationContext( {"baobaotao-dao.xml" }) //① 初始化Spring容器
public class UserDaoTest extends UnitilsJUnit4 {

@SpringBean("jdbcUserDao") //② 從Spring容器中載入DAO
private UserDao userDao;

@Before
public void init() {

}

}

在①處,通過Unitils提供@ SpringApplicationContext註解載入Spring配置檔案,並初始化Spring容器。在②處,通過@SpringBean註解從Spring容器載入一個使用者DAO例項。編寫UserDaoTest測試基礎模型之後,接下來就編寫查詢使用者資訊findUserByUserName()的測試方法。

程式碼清單16 31 UserDaoTest.findUserByUserName()測試
import org.unitils.core.UnitilsException;
import org.unitils.DbUnit.datasetfactory.DataSetFactory;
import org.unitils.DbUnit.util.MultiSchemaDataSet;

public class UserDaoTest extends UnitilsJUnit4 {


@Test //① 標誌為測試方法
@DataSet("BaobaoTao.Users.xls") //② 載入準備使用者測試資料
public void findUserByUserName() {
User user = userDao.findUserByUserName("tony"); //③ 從資料庫中載入tony使用者
assertNull("不存在使用者名稱為tony的使用者!", user);
user = userDao.findUserByUserName("jan"); //④ 從資料庫中載入jan使用者
assertNotNull("jan使用者存在!", user);
assertEquals("jan", user.getUserName());
assertEquals("123456",user.getPassword());
assertEquals(10,user.getCredits());
}

}


在①處,通過JUnit提供@Test註解,把當前方法標誌為可測試方法。在②處,通過Unitils提供的@DataSet註解從當前測試類UserDaoTest.class所在的目錄尋找支援DbUnit的資料集檔案並進行載入。執行測試邏輯之前,會把載入的資料集先持久化到測試資料庫中,具體載入資料集的策略詳見上文“配置資料集載入策略”部分。例項中採用的預設載入策略,即先刪除測試資料庫對應表的資料再插入資料集中的測試資料。這種策略可以避免不同測試方法載入資料集相互干擾。在③處執行查詢使用者方法時,測試資料庫中t_user表資料已經是如圖16-8 BaobaoTao.Users.xls所示的資料,因此查詢不到“tony”使用者資訊。在④處,執行查詢“jan”使用者資訊,從測試資料集可以看出,可以載入到“jan”的詳細資訊。最後在IDE中執行UserDaoTest. findUserByUserName()測試方法,按我們預期通過測試,測試結果如圖16-11所示。
[img]http://dl.iteye.com/upload/attachment/0066/4687/645d9fe1-4154-33a0-95c4-fefba103bc7a.jpg[/img]

完成了查詢使用者的測試之後,我們開始著手編寫儲存使用者資訊的測試方法,詳細的實現程式碼如下所示。
import org.unitils.core.UnitilsException;
import org.unitils.DbUnit.datasetfactory.DataSetFactory;
import org.unitils.DbUnit.util.MultiSchemaDataSet;

public class UserDaoTest extends UnitilsJUnit4 {


@Test //① 標誌為測試方法
@ExpectedDataSet("BaobaoTao.ExpectedSaveUser.xls") //準備驗證資料
public void saveUser()throws Exception {
User u = new User();
u.setUserId(1);
u.setUserName("tom");
u.setPassword("123456");
u.setLastVisit(getDate("2011-06-06 08:00:00","yyyy-MM-dd HH:mm:ss"));
u.setCredits(30);
u.setLastIp("127.0.0.1");
userDao.save(u); //執行使用者資訊更新操作
}

}

在①處,通過JUnit提供@Test註解,把當前方法標誌為可測試方法。在②處,通過Unitils提供的@ExpectedDataSet註解從當前測試類UserDaoTest.class所在的目錄尋找支援DbUnit的驗證資料集檔案並進行載入,之後驗證資料集裡的資料和資料庫中的資料是否一致。在UserDaoTest.saveUser()測試方法中建立一個User例項,並設定與圖16-10 驗證資料集中相同的資料,然後執行儲存使用者操作。最後在IDE中執行UserDaoTest.saveUser()測試方法,執行結果如圖16-12所示。
[img]http://dl.iteye.com/upload/attachment/0066/4689/ba05b955-118c-368c-84d7-705a469b2d20.jpg[/img]
雖然已經成功完成了儲存使用者資訊UserDaoTest.saveUser() 方法測試,但還是存在不足的地方,我們測試資料通過硬編碼方式直接設定在User例項中。如果需要更改測試資料,只能更改測試程式碼。大大削減了測試的靈活性。如果能直接從Excel資料集獲取測試資料,並自動繫結到目標物件,那我們的測試用例就更加完美。為此筆者編寫了一個獲取Excel資料集Bean工廠XlsDataSetBeanFactory,用於自動繫結資料集到測試物件。我們對上面的測試方法進行整改,實現程式碼如程式碼清單16-33所示。
import org.unitils.core.UnitilsException;
import org.unitils.DbUnit.datasetfactory.DataSetFactory;
import org.unitils.DbUnit.util.MultiSchemaDataSet;
import sample.unitils.dataset.util.XlsDataSetBeanFactory;

public class UserDaoTest extends UnitilsJUnit4 {


@Test //① 標誌為測試方法
@ExpectedDataSet("BaobaoTao.ExpectedSaveUser.xls") //準備驗證資料
public void saveUser()throws Exception {

//② 從儲存資料集中建立Bean
User u = XlsDataSetBeanFactory.createBean("BaobaoTao.SaveUser.xls”
,"t_user", User.class);
userDao.save(u); //③ 執行使用者資訊更新操作
}

}

在②處,通過XlsDataSetBeanFactory.createBean()方法,從當前測試類所在目錄載入BaobaoTao.SaveUser.xls資料集檔案,其資料結構如圖16-9所示。把BaobaoTao.SaveUser.xls中名稱為t_user 的Sheet頁中的資料繫結到User物件,如果當前Sheet頁有多條記錄,可以通過XlsDataSetBeanFactory.createBeans()獲取使用者列表List<User>。最後在IDE中重新執行UserDaoTest.saveUser()測試方法,執行結果如圖16-13所示。
[img]http://dl.iteye.com/upload/attachment/0066/4692/0530e9b9-41d6-351f-b3b7-6c065d130846.jpg[/img]
從測試結果可以看出,執行UserDaoTest.saveUser()測試失敗。從右邊的失敗報告資訊我們可以看出,是由於模擬使用者的積分與我們期望資料不一致造成,期望使用者積分是30,而我們儲存使用者的積分是10。重新對比一下圖16-9 BaobaoTao.SaveUser.xls資料集資料與圖16-10 BaobaoTao.ExpectedSaveUser.xls資料集的資料,確實我們準備儲存資料集的資料與驗證結果的資料不一致。把BaobaoTao.SaveUser.xls資料集中的使用者積分更改為30,最後在IDE中重新執行UserDaoTest.saveUser()測試方法,執行結果如圖16-14所示。
[img]http://dl.iteye.com/upload/attachment/0066/4698/0c4d2ace-dedf-36a6-b16b-39f75348b75b.jpg[/img]
從測試結果可以看出,儲存使用者通過測試。從上述的測試實戰,我們已經體驗到用Excel準備測試資料與驗證資料帶來的便捷性。到此,我們完成了DAO測試的整個過程,對於XlsDataSetBeanFactory具體實現,讀者可以檢視本章的例項原始碼,這裡就不做詳細分析。下面是實現基本骨架。
import org.dbunit.dataset.Column;
import org.dbunit.dataset.DataSetException;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.ITable;
import org.dbunit.dataset.excel.XlsDataSet;

public class XlsDataSetBeanFactory {

//從Excel資料集檔案建立多個Bean
public static <T> List<T> createBeans(String file, String tableName,
Class<T> clazz) throws Exception {
BeanUtilsBean beanUtils = createBeanUtils();
List<Map<String, Object>> propsList = createProps(file, tableName);
List<T> beans = new ArrayList<T>();
for (Map<String, Object> props : propsList) {
T bean = clazz.newInstance();
beanUtils.populate(bean, props);
beans.add(bean);
}
return beans;
}

//從Excel資料集檔案建立多個Bean
public static <T> T createBean(String file, String tableName, Class<T> clazz)
throws Exception {
BeanUtilsBean beanUtils = createBeanUtils();
List<Map<String, Object>> propsList = createProps(file, tableName);
T bean = clazz.newInstance();
beanUtils.populate(bean, propsList.get(0));
return bean;
}

}



這些文章摘自於我的[b][color=red]《Spring 4.x企業應用開發實戰》[/color][/b]的第16章,我將通過連載的方式,陸續在此發出。歡迎大家討論。