現(xiàn)實系統(tǒng)中通常會有一些具有外部依賴性的對象,這些對象和數(shù)據(jù)庫或者其他對象存在諸多關聯(lián)。如果我們對這樣的對象編寫單元和組件級測試的話,可以想象將是非常麻煩的一件事.因為這種外部依賴性的存在,使的我們很難將對象孤立出來進行測試。經(jīng)常提及的白盒測試法,基本上是通過控制對象的外部依賴性來達到隔離對象的目的,使的可以操作這些對象的狀態(tài)和相關行為!
運用 模擬對象(mock objects) 或者stubs,是一個控制對象外部依賴性的解決方案。通過隔離那些關聯(lián)的數(shù)據(jù)庫訪問類,象JDBC的相關操作類,對于控制對象外部依賴性將是很有效的。但模擬對象的解決方案對一些特殊的應用系統(tǒng)架構顯得力不從心了,象那些運用了EJB的CMP(container-managed persistence)或者 JDO(java Data Objects)的應用系統(tǒng)架構,在這些架構里,數(shù)據(jù)庫的訪問對象是在底層的而且很隱蔽!
由Manuel Laflamme編寫的開放源代碼的DBUnit架構體系,對于控制系統(tǒng)內(nèi)部的數(shù)據(jù)庫依賴性提供了一個非常不錯的解決方案。他允許程序員在整個的測試過程中自由的管理控制數(shù)據(jù)庫的狀態(tài),這很重要。利用DBUnit,在測試之前,我們可以給目標數(shù)據(jù)庫植入我們需要的數(shù)據(jù)集,而且,在測試完畢后,數(shù)據(jù)庫完全能夠回溯到測試前的狀態(tài)。
在很多成功的軟件項目中,測試自動化往往是關鍵的層面。DBUnit允許開發(fā)人員創(chuàng)建測試用例代碼,在這些測試用例的生命周期內(nèi)我們可以很好的控制數(shù)據(jù)庫的狀態(tài)。而且,這些測試用例是很容易實現(xiàn)自動化的。這樣在測試過程中我們無須對它進行人工的干預,為人工干預造成的后果而擔心更沒必要了!
簡單介紹
配置使用DBUnit的第一步我們首先需要知道如何生成數(shù)據(jù)庫schema,這個文件是XML格式的,其中包括了數(shù)據(jù)庫的表及相關數(shù)據(jù)信息!
例如,這里有一個數(shù)據(jù)庫表employee,我們可以用SQL的形式這樣將他表示出來。而且,我們可以看到,一個簡單的數(shù)據(jù)集可以這樣表示在DBUnit中,上面這個表和抽樣數(shù)據(jù)信息可以用XML文件的形式這樣表示:
<EMPLOYEE employee_uid='1' start_date='2001-11-01' first_name='Andrew' ssn='xxx-xx-xxxx' last_name='Glover' />
這個生成的XML格式的文件可以作為系統(tǒng)所需的所有種子文件(seed files)的樣本模版.
為相互關聯(lián)的測試場景創(chuàng)建多個種子文件是一個很有效的策略,象通過不同的數(shù)據(jù)庫文件來區(qū)分隔離數(shù)據(jù)庫狀態(tài)是一個道理。多種子文件策略可以將我們的測試目標鎖定到較小的范圍,目標數(shù)據(jù)可以只針對數(shù)據(jù)庫的表,而不是整個數(shù)據(jù)庫!
為了給目標數(shù)據(jù)庫植入不同的職員記錄,我們需要的XML數(shù)據(jù)文件如下所示:
<?xml version='1.0' encoding='UTF-8'?>
<dataset>
<EMPLOYEE employee_uid='1'
start_date='2001-01-01'
first_name='Drew' ssn='000-29-2030'
last_name='Smith' />
<EMPLOYEE employee_uid='2'
start_date='2002-04-04'
first_name='Nick' ssn='000-90-0000'
last_name='Marquiss' />
<EMPLOYEE employee_uid='3'
start_date='2003-06-03'
first_name='Jose' ssn='000-67-0000'
last_name='Whitson' />
</dataset>
現(xiàn)在,要讓DBUnit和我們所需的數(shù)據(jù)庫schema一起工作了,對于程序員來說,我們使用DBUnit進行測試可以有兩種選擇:通過直接編碼方式進行測試或者與Ant結合.
編碼方式
DBUnit框架提供了一個基本的抽象測試用例類,叫做DatabaseTestCase,它是JUnit框架中的基礎類TestCase的子類。如果我們使用這個類必須首先實現(xiàn)兩個鉤子方法(hook
methods):getConnection()和getDataSet().
方法getConnection()需要返回一個IDatabaseConnection類型的對象,這個對象是一個基于普通JDBC連接的包裝類。例如,下面的代碼段演示了在MySQL數(shù)據(jù)庫環(huán)境下,IDatabaseConnection類型連接對象的創(chuàng)建方法!
protected IDatabaseConnection getConnection()
throws Exception {
Class driverClass = Class.forName("org.gjt.mm.mysql.Driver");
Connection jdbcConnection = DriverManager.getConnection("jdbc:mysql://127.0.0.1/hr", "hr", "hr");
return new DatabaseConnection(jdbcConnection);
}
方法getDataSet()返回一個IDataSet類型對象,其實,說白了,他是我們先前提到的XML數(shù)據(jù)的種子文件的另一種表現(xiàn)形式。
protected IDataSet getDataSet() throws Exception {
return new FlatXmlDataSet(
new
FileInputStream("hr-seed.xml"));
}