數(shù)據(jù)訪問對象(Data Access Object)模式已經(jīng)成為 J2EE 開發(fā)人員工具庫中的標準部件。大多數(shù)開發(fā)人員不知道它有一個變體可以使測試更輕松。 模擬數(shù)據(jù)訪問對象集中了 DAO、模仿對象和分層測試的精華,從而允許您同時改進測試結(jié)果和整體開發(fā)方法。企業(yè) Java 開發(fā)人員(并且是 SDAO 大師)Kyle Brown 使用代碼樣本和討論向您全面介紹 SDAO 的概念和日常用法。
今年早些時候,我在北卡羅萊納州立大學(North Carolina State University)給一群研究生作了一個關(guān)于 J2EE 技術(shù)的演講。我提供了一個說明 servlet 和 JSP 用法的樣本設計,以說明如何結(jié)合 MVC 方法和簡單數(shù)據(jù)訪問對象(DAO)以處理應用程序中持久數(shù)據(jù)的查詢和更新。在陳述這些材料時,我看到聽眾們會心地微笑和點頭。但是,在陳述結(jié)束之后,我收到了一個學生的電子郵件。為什么我實現(xiàn)的 DAO 不止一個,而是兩個,又為什么設置了一個 Factory
類以允許在它們之間進行選擇呢?
這很令人費解,但是我很快意識到了問題所在:我忘了告訴那些學生,我正在實現(xiàn) MVC 和 DAO 之外的第三種模式。尤其是,我的設計利用了模仿對象來簡化測試。在我考慮 DAO 的方式中,模仿對象是很基本的,以致我無法想象在缺少其中一個的情況下如何實現(xiàn)另一個。進一步說,我自動地調(diào)整了設計,以便使測試更容易,這對許多經(jīng)驗豐富的開發(fā)人員和我的學生們都是一個同樣陌生的概念。
在本文中,您將了解使用模擬數(shù)據(jù)訪問對象(SDAO)進行分層測試。與模仿對象一樣,SDAO 用專門為測試開發(fā)的對象取代現(xiàn)有對象。但是,與模仿對象不同的是,不需要對 SDAO 提供檢測以包含測試斷言。SDAO 模式的目的是允許您將一個層(域?qū)ο髮樱┖土硪粋€層(數(shù)據(jù)訪問層)隔離開。我們將從回顧數(shù)據(jù)訪問對象模式入手。
關(guān)于模式的只言片語 |
數(shù)據(jù)訪問對象
數(shù)據(jù)訪問對象模式的目的是提供到特定數(shù)據(jù)源的單個聯(lián)系點。與許多設計模式相似,DAO 以分隔設計任務為基礎。具體來說,DAO 將業(yè)務邏輯與數(shù)據(jù)庫持久性代碼分隔開來。數(shù)據(jù)訪問對象負責對持有數(shù)據(jù)(存儲在關(guān)系數(shù)據(jù)庫中)的對象進行操作。DAO 所操作的對象通常稱為 值對象,盡管術(shù)語 數(shù)據(jù)傳送對象(data transfer object,DTO)的意思更為貼切。
DAO 類通常包含對其 DTO 進行操作的 CRUD 方法,即 create()
、 read()
、 update()
和 delete()
。例如,假定我們正在構(gòu)建一個登記會議出席人員的系統(tǒng)。我們的 DAO 類接口可能如清單 1 中所示:
|
聲明這個接口(或與它類似的接口)是實現(xiàn) DAO 模式的標準部分。具體的 DAO 應用程序?qū)崿F(xiàn)如圖 1 中所示的接口:
消息處理
在大多數(shù)情況下,類似于上述示例的 DAO 應用程序會使用 JDBC 進行數(shù)據(jù)庫訪問。例如,在 getAllAttendees()
方法的示例中,該類將獲取一個到數(shù)據(jù)庫的連接,查詢數(shù)據(jù)庫以獲取 Attendee
表中的行列表,迭代從查詢返回的 ResultSet
,然后為 ResultSet
中每一行構(gòu)造 Attendee
對象。
請參閱 參考資料以獲取 DAO 模式的深入討論和示例。
模擬數(shù)據(jù)訪問對象
模擬數(shù)據(jù)訪問對象實際上模擬后端數(shù)據(jù)存儲。實現(xiàn) SDAO 模式使我們能夠測試各種應用程序?qū)樱ㄈ鐦I(yè)務邏輯和 GUI),而無需恰好擁有實際的數(shù)據(jù)庫。
以下是使用 SDAO 進行分層測試的一些具體優(yōu)點:
TransactionRollbackException
)難以定位;從綜合體中除去數(shù)據(jù)庫層讓您更快地找出真實的問題。 SDAO 實戰(zhàn)
理解 SDAO 如何工作的最佳方法是實際研究它,并希望您親自應用它。我們將從模擬數(shù)據(jù)訪問對象的簡單示例入手,如清單 2 所示:
|
現(xiàn)在來看一下,這是晦澀的火箭科學,是嗎? DefaultDAO
類在靜態(tài)變量 instance
中存儲了它自己的一個實例(存儲為 Singleton),并允許通過 getInstance()
方法訪問該實例。然后,該類的用戶可以在 Singleton 實例保存的集合中添加和刪除 Attendee
元素,或替換集合中的元素。
對象工廠
要使這種技術(shù)在實際工作中有效,需要能夠?qū)⒊绦蛑械?#8220;實際”DAO 類替換成新的“模擬”DAO 類。我們的客戶機代碼本身不能引用 Db2AttendeeDAO
類或 DefaultDAO
類。因此我們使用 Factory
類(又名對象工廠),以根據(jù)需要為客戶機代碼提供 Db2AttendeeDAO
和 DefaultDAO
實例。
我們的對象工廠相當簡單。它只返回兩個類實例,用一種軟件“開關(guān)”(如 getAttendeeDAO()
方法中所示)在兩者之間進行切換。這個開關(guān)還可以檢查 System
特性的值,或檢查一些其它全局值,如清單 3 所示:
|
當您運行測試時,通常首先將 Factory
開關(guān)設置成返回模擬類。這樣做確保您可以在與數(shù)據(jù)庫隔離的情況下測試系統(tǒng)的其余層。只有在后面的測試中才會將開關(guān)設置成返回“實際”DAO。圖 2 使您對最終設計(包括 Factory
類)有一些了解,有可能如下所示:
高級 SDAO
在您理解了基本的 SDAO 實現(xiàn)之后,就可以研究其它使用模擬 DAO( DefaultDAO
)類的方法。迄今為止,您所看到的只是最簡單的實現(xiàn),其中的結(jié)果取自內(nèi)存中的集合,該集合在測試期間必須被填充。這種思想的常見擴展是在類的構(gòu)造函數(shù)中預先用缺省值填充該集合。正如我們在此所做的,使用 Singleton 的主要缺點是:您必須在各次測試之間清除 Singleton。如果您在某次測試時忘了這樣做,則會導致后面的測試失敗。幸運的是,大多數(shù)單元測試框架(如 JUnit)提供了輕松進行此類測試的工具。例如,在 JUnit 中,可以將清除 Singleton 的代碼放入測試類的 teardown()
方法中,并將任何執(zhí)行預先填充工作的代碼放到該測試類的 setUp()
方法中。
第二種方法略微復雜些,但也是為更實際的測試所提供的,這就是使用 Java 序列化或 XML 從文件讀取一組對象。這兩種技術(shù)都允許您使用幾個文件來表示同一個測試的不同初始條件。
我經(jīng)常在 SDAO 測試中使用“分兩步走”的方法。我構(gòu)建的第一個 DAO 是“缺省”DAO;它轉(zhuǎn)至 DTO 內(nèi)存中集合,然后被傳遞給構(gòu)建應用程序中上層部分(例如 servlet 和 JSP 文件)的團隊。然后我和另一個團隊一起構(gòu)建將實際使用數(shù)據(jù)庫的 DAO。這種方法允許兩個團隊同時工作,他們的交互是由 DAO 接口的共享約定定義的。
結(jié)束語
在 IBM WebSphere 軟件服務組(Software Services for WebSphere group)中,我們已經(jīng)成功地將本文描述的分層測試技術(shù)應用到了數(shù)十個客戶合作項目中。除了改進我們的整個產(chǎn)品之外,SDAO 還成為了幫助我們團隊掌握各種 J2EE API 特性的重要工具。使用模擬和實際 DAO 使我們可以在應用程序的許多層上同時工作,而不會被一次性地將所有部分組裝到一起的復雜情況所“嚇倒”。
作者衷心地感謝 Stacy Joines 和 Ken Hygh 對本文提出的有益建議。
關(guān)于作者 Kyle Brown 是 IBM WebSphere 軟件服務部(Software Services for WebSphere)的高級技術(shù)組成員。Kyle 向財富 500 強客戶提供關(guān)于面向?qū)ο笾黝}和 Java 2 企業(yè)版(J2EE)技術(shù)的咨詢服務、培訓和指導。他與別人合著了 Enterprise Java Programming with IBM WebSphere 、 WebSphere 4.0 AEs Workbook for Enterprise JavaBeans(第 3 版) 和 The Design Patterns Smalltalk Companion 。他還經(jīng)常在研討會上發(fā)表關(guān)于企業(yè) Java、OO 設計和設計模式的演講。您可以通過 mailto:brownkyl@us.ibm.com?subject=Stepped approach to J2EE testing with SDAO與他聯(lián)系。 |