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