iBATIS中的單元測試

DAO消費者

持久化邏輯

     單元測試已經成爲了現代軟件開發方法中的一個非常重要的組成部分。即使不贊成極限編程(eXtreme Programming, XP)或者其他敏捷方法能夠帶來好處,單元測試也應該成爲你的軟件開發生命週期中的一個基礎實踐。
從概念上說,持久層可以分爲3層,而iBATIS使得對這些不同的層進行單元測試都變得非常簡單,如圖13-1所示。

SQL映射

  • BATIS至少在以下3個方面可以使得你對這些不同層進行單元測試變得更容易:
  • 測試映射層(mapping layer)本身,包括各個映射、所有的SQL語句,以及這些SQL語句被映射到的那些領域對象。
  • 測試DAO層,這使你可以對DAO層中的任何特定於持久化的邏輯進行測試。
  • 在DAO的消費層中進行測試。

13.1.1 對映射層進行單元測試
      對映射層所進行的單元測試,可能是在大部分應用程序中所發生的最低層次的單元測試了。此過程包括對SQL語句以及這些語句所映射到的領域對象進行測試。這意味着我們將需要一個用於進行測試的數據庫實例。

2 1. 測試用數據庫實例

     測試用數據庫實例可能是創建於你實際使用的數據庫管理系統(例如,Oracle或者微軟的SQL Server)中的一個真實實例。如果你的環境對於單元測試是友好的,那麼只需要簡單地更改一下配置就可以運行單元測試了。如果打算使用非標準數據庫特徵 (例如,存儲過程),那麼就可能有必要使用真實的數據庫實例。存儲過程和其他一些非可移植的數據庫設計時選擇,會使得對數據庫進行單元測試變得很難,除非 使用真實的數據庫實例。
      使用真實的數據庫實例的缺點是,只有連接到網絡才能進行單元測 試。或者也可以使用某個真實數據庫的一個本地實例,但這意味着單元測試在運行之前將需要額外的本地環境設置。無論使用這兩種方法中的哪一種,你都將面對同 一個問題,即每次測試都必須重建測試數據,甚至可能需要重建測試套件(test                           suite)之間的模式,或者是每個單元測試之間的模式。即使是在大型的企業級的數據庫服務器上,要完成以上任務也需要花費很長時間。另一個問題是,由於 使用的數據庫是集中式的,多個開發人員同時進行單元測試時就可能會導致衝突。所以,必須使用不同的數據庫模式來隔離每一個開發人員。正如你所見,這種方法 的普遍問題就是,單元測試取決於相當多的基礎設施,而這對於大多數經驗豐富的測試驅動程序的開發人員來說是不夠完美的。
     Java開發人員是非常幸運的,因爲他們至少有一種非常棒的內存 (in-memory)數據庫可以使用,這種數據庫可以使得對相對標準的數據庫設計進行單元測試變得非常簡單。HSQLDB是一個完全用Java寫成的內 存數據庫。它既不需要磁盤上的任何文件也不需要連接網絡就能夠正常工作。此外,它還能夠重新生成來自典型數據庫(例如Oracle和微軟的SQL Server)的大部分數據庫設計。即使由於設計過於複雜(例如使用了存儲過程)而導致HSQLDB不能重建整個數據庫,它也仍然能夠重新生成該數據庫的 絕大部分。HSQLDB允許快速重建數據庫,包括數據庫模式和測試數據。iBATIS自己的單元測試套件就是使用HSQLDB在每個單獨的測試之間重建數 據庫模式和測試數據。我們親自使用HSQLDB測試了由將近1000個數據庫相關的測試構成的測試套件,運行時間不到30秒。
      有關HSQLDB的更多信息,請訪問網頁http://hsqldb.sourceforge.net/。另外,可能會讓微軟的.NET高興的一個消息是,已經有人發起對HSQLDB的移植了,同時也有人開始創建其他的內存數據庫了。


3 2. 數據庫腳本
      現在已經有了數據庫實例,那麼數據庫模式和測試數據應該如何構造和創建呢?你可能已經有了可以用來創建數據庫模式以及測試數據的數據庫腳本了。理想情況 下,你應該將這些腳本納入到版本控制系統(例如CVS或者Subversion)中。這些腳本應該和應用程序中的其他代碼一樣被同等地對待。即使你對自己 使用的數據庫沒有控制權,你也應該定期從擁有控制權的人那裏獲得相應的更新。應用程序的源代碼與數據庫腳本應該始終保持同步,並且單元測試本來就是確保它 們同步的。每當運行單元測試套件時,你還應該運行這些腳本來重新創建數據庫模式。使用這種方法,可以很容易地將數據庫創建腳本需要的新集提交給版本控制系 統,然後運行單元測試以便確定腳本更新是否給應用程序帶來了問題。這是最理想的情況。如果使用內存關係數據庫(例如HSQLDB)來運行測試,則可能需要 另外一個步驟來轉換數據庫模式。可以考慮使這個轉換過程自動化,以便避免手工編程可能出現的錯誤,加快集成的速度。

4 3. iBATIS配置文件(例如SqlMapConfig.xml)
     爲了進行單元測試,你可能想要使用一個獨立的iBATIS配置文件。配置文件用於控制數據源和事務管理器的配置,它在測試環境和產品環境中可能會完全不 同。例如,產品環境可能會是一個像J2EE應用程序服務器這樣的受管理環境。在這樣的環境下,一個受管理的DataSource實例可能是從JNDI中檢 索得到的。你還可能會在產品環境中利用全局事務。然而,在測試環境中,你的應用程序可能不會運行在服務器中;而只是配置了一個簡單DataSource, 使用的也是局部事務。分別進行測試環境配置和產品環境配置的最簡單的方式就是,使用不同的iBATIS配置文件,這兩份配置文件引用相同的一組SQL映射 文件。

5 4. iBATIS SqlMapClient單元測試
      現在所有的先決條件都已經準備好了,這些先決條件包括數據庫實例、自動構建數據庫的腳本,以及用於測試的配置文件,接下來可以開始創建單元測試了。代碼清單13-1即是一個使用JUnit來創建簡單單元測試的例子。
代碼清單13-1 SqlMapClient單元測試示例
設置單元測試和測試數據

利用主鍵值測試單個person對象的檢索

     代碼清單13-1中的示例使用了針對Java的JUnit單元測試框架。(可以在www.junit.org上找到 更多有關JUnit的信息。對於.NET Framework,也有相似的工具,包括NUnit,可以從網頁www.nunit.org上下載得到。)在我們的設置方法中,首先刪除了測試涉及的那 些數據庫表,然後再重建它們並對它們重新填充測試數據。爲每一個測試都重建所有的東西可以確保各個測試間相互獨立,但是如果在像Oracle或者SQL Server這樣的RDBMS上這樣刪刪建建地做測試那就太慢了。在這種情況下,可以使用類似於HSQLDB這樣的內存關係數據庫。在我們實際的測試案例 中,我們從數據庫中讀取一條記錄,將其映射到一個bean上,然後斷言(assert)bean中各字段的值都是所預期的值。                     

以上就是測試映射層所需做的全部工作了。接下來要測試的層是DAO層,假定你的應用程序有DAO層。

13.1.2 對DAO進行單元測試

      DAO層是一個抽象層,因此根據其本質,DAO應該非常容易測試。DAO也使得對DAO層用戶的測試變得更加簡單。 本節,將討論測試DAO本身。DAO通常被分離爲一個接口和一個實現。由於我們將直接測試DAO,因此接口將不起作用。我們將直接對DAO實現進行測試。 這可能同DAO模式的工作方式是相反的,但是這恰好體現了單元測試的好處——它把那些壞習慣清除出我們的系統!
      如果可能的話,對DAO層的測試應該不涉及數據庫以及底層的基礎設施。DAO層是持久化實現的一個接口,但是在測試DAO層時,我們更感興趣的是測試DAO層內部的東西,而不是測試DAO層之外的東西。
      測試DAO的複雜度僅僅取決於DAO實現。例如,測試一個JDBC DAO可能非常困難。你需要一個很好的模擬框架來代替所有典型的JDBC組件,如Connection、ResultSet和Prepared-   Statement等。即使是這樣,要利用模擬對象來管理這樣複雜的API也非常麻煩。而模擬iBATIS SqlMapClient接口則簡單得多。下面就來試一試。

6 1. 利用模擬對象來對DAO進行單元測試

      模擬對象是指爲了進行單元測試而用來替換實際實現的對象。模擬對象通常沒有很強的功能性;它們只用於滿足某個單一的情況,以使得單元測試僅僅關注其應該關注的部分,而不需要擔心複雜度的增加。我們將在下面的例子中使用這些模擬對象來示範一種測試DAO層的方法。
      在我們的示例中,將使用一個簡單的DAO。我們將不考慮iBATIS DAO框架,因此就不需要擔心事務以及諸如此類的東西了。這個示例的目的就是,示範如何測試DAO層,無論你使用的是哪一種DAO框架(只要確實使用了它)。
      首先,來考察一下要進行測試的DAO。代碼清單13-2給出了一個SqlMapPersonDao實現,它調用了一個和13.1.1節中給出的示例相似的SQL映射文件。
      代碼清單13-2 測試一個簡單的DAO

      請注意在代碼清單13-2中我們是如何把SqlMapClient注入到DAO的構造函數中的。這爲對DAO進行單元測試提供了一種簡易的方式,因爲我們 只需要模擬SqlMapClient接口就可以了。顯然,這是一個非常簡單的示例,沒有對其進行太多的測試,但是每一個測試都非常重要。代碼清單13-3 顯示了用來模擬SqlMapClient並且測試getPerson()方法的單元測試。
代碼清單13-3 含有模擬SqlMapClient的PersonDao單元測試

     代碼清單13-3中給出的示例使用了JUnit以及JMock這個Java對象模擬框架。正如代碼清單13-3中加粗的部分所顯示的那樣,利用JMock 來模擬SqlMapClient接口實現,使我們能夠單獨測試DAO的行爲,而無需顧慮實際的SqlMapClient實現,也就不需要考慮與其相關的 SQL語句、XML文件,還有數據庫了。JMock是一個非常好用的工具,可以在www.jmock.org上找到更多有關它的信息。你可能已經猜到了, 還有一個針對.NET的模擬框架,稱爲NMock,有關它的更多信息請參考http://nmock.org

13.1.3 對DAO的消費層進行單元測試
      應用程序中那些使用DAO層的其他層稱爲DAO層的消費者(consumer)。DAO模式使得你可以在不依賴於持久層的任何功能的情況下測試這些消費者 的功能。一個好的DAO實現應該有一個能夠很好地描述其可用功能的接口。測試消費層的關鍵在於獲得此接口。考察代碼清單13-4中的接口,你就會發現上一 節中所描述的getPerson()方法。
代碼清單13-4 簡單的DAO接口

      要開始測試DAO層的消費者,所需要的就只是代碼清單13-4中所給出的接口。我們甚至根本不需要一個完整的實現。利用JMock,我們能夠很輕鬆地模擬getPerson()方法所預期的行爲。考慮如下這個使用了PersonDao接口的服務(見代碼清單13-5)。
代碼清單13-5 使用PersonDao接口的服務


      我們的單元測試的目標並不是DAO——而是getValidatedPerson()方法中的業務邏輯,例如它所執行的各種驗證。方法中的每一個驗證可能都是一個私有方法,爲了便於討論,假設此處我們只測試私有接口。
      多虧了之前的PersonDao接口,在沒有數據庫的情況下測試 getValidatedPerson()方法也很容易。所需要做的只是模擬PersonDao接口實現,然後將該模擬實現傳遞給服務的構造函數,最後調 用getValidatedPerson()方法即可。代碼清單13-6給出了完成上述工作的單元測試。
代碼清單13-6 使用模擬而不是真實的DAO,以避免訪問數據庫

     我們再一次同時使用了JUnit和JMock。正如你在代碼清單13-6中所見到的那樣,這種測試方式在應用程序的各個層都是一致的。這是很有好處的,因爲它可以帶來易於維護的凝練的單元測試。      
     有關iBATIS中的單元測試,我們就介紹到這。實際上網上有很多很好的關於單元測試資源。使用Google搜索一下“unit test(單元測試)”,就可以找到很多相關的資源,它們可以幫助你迅速提高單元測試的能力,甚至你還可能發現比本書使用的方法更好的單元測試方法。

 

http://www.cnblogs.com/newstar/archive/2011/11/27/2264687.html

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