邊城愚人

          如果我不在邊城,我一定是在前往邊城的路上。

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            31 隨筆 :: 0 文章 :: 96 評論 :: 0 Trackbacks

          Mock 對象能夠模擬領域對象的部分行為,并且能夠檢驗運行結果是否和預期的一致。領域類將通過與 Mock 對象的交互,來獲得一個獨立的測試環境(引自《 精通 Spring——Java 輕量級架構開發實踐 》。

          在模仿對象中,我們定義了四個概念:

          1 目標對象:正在測試的對象

          2 合作者對象:由目標對象創建或獲取的對象

          3 模仿對象:遵循模仿對象模式的合作者的子類(或實現)

          4 特殊化對象:覆蓋創建方法以返回模仿對象而不是合作者的目標的子類

          一般來說,應用模仿對象的過程如下:

          1 )創建模仿對象的實例

          2 )設置模仿對象中的狀態和期望值

          3 )將模仿對象作為參數來調用域代碼

          4 )驗證模仿對象中的一致性

          那么,我們應該如何以及在哪里使用 Mock 對象呢?一般來說,對于目標對象中的合作者對象,在測試時如果其狀態或行為的實現嚴重地依賴外部資源(比如數據持久化中的 DAO ,比如負責發送電子郵件的類),或者團隊并行開發時,目標對象的合作者對象并沒有實現(比如 J2EE 中,橫向分工時,負責 Action 的調用 Service ,負責 Service 調用 DAO 時,相應的 Service DAO 沒有實現),這時我們就需要模仿這些類。其實,在做 J2EE 時,傳統的 N 層架構中,我們都是面向接口編程的,我們定義了 DAO 接口,我們定義了 Service 接口,這樣做的優點就是我們在測試時可以構造實現接口的 Mock 類。這里不得不提依賴注入,通過依賴注入,我們才能在測試時 set Mock 對象。這也說明,為了方便測試,我們不得不一步一步 重構代碼,而模式就在重構中自然地產生了。

          對于 Mock 對象,我們可以根據 合作者接口(或者是類) 實現具體的 Mock 類,這樣的 Mock 類實際上是 Stub 。有些情況下, Stub 是必要的。但對于諸如 DAO 、 Service ,我們只關心在給定參數的情況下,調用的方法能夠返回預期的值,我們根本不關心其內部實現,這時候如果使用 Stub 的話就會產生不必要的代碼。我們需要的就是能 動態地生成 Mock 對象而不需要編寫它們的工具, EasyMock 就是這樣的工具。

          EasyMock 是一個 Mock 對象的類庫,現在的版本是 2.0 ,這個版本只支持 Mock 接口,如果需要 Mock 類,需要下載它的擴展包。

          下面通過一個具體的例子說明一下 Mock 對象的使用,我寫的例子就是測試 Service 類中的一個方法, Mock 的對象是 DAO 。

          首先是一個簡單的實體 bean Account:

          package ?easymocktest.domain;

          import
          ?org.apache.commons.lang.builder.ToStringBuilder;
          import
          ?org.apache.commons.lang.builder.ToStringStyle;

          public ? class ?Account? {

          ????
          private ?Long?id;
          ????
          private ?String?name;
          ????
          private ?String?pwd;
          ????
          ????
          public ?Long?getId()? {
          ????????
          return ?id;
          ????}

          ????
          public ? void ?setId(Long?id)? {
          ????????
          this .id? = ?id;
          ????}

          ????
          public ?String?getName()? {
          ????????
          return ?name;
          ????}

          ????
          public ? void ?setName(String?name)? {
          ????????
          this .name? = ?name;
          ????}

          ????
          public ?String?getPwd()? {
          ????????
          return ?pwd;
          ????}

          ????
          public ? void ?setPwd(String?pwd)? {
          ????????
          this .pwd? = ?pwd;
          ????}

          ????
          /**
          ?????*?
          @see ?java.lang.Object#toString()
          ?????
          */

          ????
          public ?String?toString()? {
          ????????
          return ? new ?ToStringBuilder( this ,?ToStringStyle.MULTI_LINE_STYLE)
          ????????????????.append(
          " name " ,? this .name).append( " pwd " ,? this .pwd).append( " id " ,
          ????????????????????????
          this .id).toString();
          ????}

          ????
          }

          ??????

          ????一個只含一個方法的 DAO AccountDAO:

          package ?easymocktest.dao;

          import ?easymocktest.domain. *
          ;
          public ? interface ?AccountDAO? {
          ????
          public ?Account?getByNameAndPwd(String?name,String?pwd);
          }

          ????

          ?

          ???一個同樣只含一個方法的 AccountService 接口:

          ?

          package ?easymocktest.service;

          import ?easymocktest.domain. *
          ;
          public ? interface ?AccountService? {
          ????
          public ?Account?getAccount(String?name,String?pwd);
          }

          ???與 AccountService 相應的實現類:

          package ?easymocktest.service.impl;

          import
          ?easymocktest.service.AccountService;
          import ?easymocktest.dao. *
          ;
          import
          ?easymocktest.domain.Account;
          public ? class ?AccountServiceImpl? implements ?AccountService? {

          ????
          private ?AccountDAO?accountDAO;

          ????
          public ?AccountDAO?getAccountDAO()? {
          ????????
          return ?accountDAO;
          ????}


          ????
          public ? void ?setAccountDAO(AccountDAO?accountDAO)? {
          ????????
          this .accountDAO? = ?accountDAO;
          ????}


          ????
          public ?Account?getAccount(String?name,?String?pwd)? {
          ????????
          return ? this .accountDAO.getByNameAndPwd(name,?pwd);
          ????}


          }


          ???這里我沒有實現 AccountDAO 接口,對于 Mock 測試來說,這也是不需要的。下面就是 AccountServiceImpl 的測試類 AccountServiceTest
          ???

          package ?easymocktest.service;

          import ?junit.framework. *
          ;
          import ?easymocktest.dao. *
          ;
          import ?easymocktest.domain. *
          ;
          import ?easymocktest.service.impl. *
          ;
          import ? static
          ?org.easymock.EasyMock.createMock;
          import ? static
          ?org.easymock.EasyMock.replay;
          import ? static
          ?org.easymock.EasyMock.reset;
          import ? static
          ?org.easymock.EasyMock.verify;
          import ? static
          ?org.easymock.EasyMock.expect;
          public ? class ?AccountServiceTest? extends ?TestCase {

          ????
          private ?AccountDAO?accountDAOMock;
          ????
          private ?AccountServiceImpl?accountService;
          ????
          ????@Override
          ????
          protected ? void ?setUp()? throws ?Exception? {
          ????????accountDAOMock?
          = ?createMock(AccountDAO. class );
          ????????accountService?
          = ? new ?AccountServiceImpl();
          ????????accountService.setAccountDAO(accountDAOMock);
          ????}

          ????
          ????
          public ? void ?testGetAccount() {
          ????????String?name?
          = ? " kafka " ;
          ????????String?pwd?
          = ? " 0102 " ;
          ????????Account?a?
          = ? new ?Account();
          ????????a.setName(name);
          ????????a.setPwd(pwd);
          ????????a.setId(
          new ?Long( 10 ));
          ????????reset(accountDAOMock);
          // (a)
          ????????expect(accountDAOMock.getByNameAndPwd(name,?pwd)).andReturn(a); // (b)
          ????????replay(accountDAOMock); // (c)
          ????????Account?b? = ?accountService.getAccount(name,?pwd);
          ????????assertEquals(a,?b);
          ????????verify(accountDAOMock);
          // (d)
          ????}

          }

          ???

          下面簡要的說明一下 Mock 對象的工作過程:

          1 )在 setUp() 中,通過 “accountDAOMock = createMock(AccountDAO.class);” (這里使用了 java5 中的靜態導入),創建 AccountDAO Mock 對象,由于 EasyMock 采用了范型技術,故創建的 Mock 對象不需要強制類型轉換。然后通過 “accountService.setAccountDAO(accountDAOMock);” 設置目標對象的合作者對象。

          2 )對于測試方法 “testGetAccount()” , (a) 處的 reset() 方法是將 Mock 對象復位,也就是重新設置 Mock 對象的狀態和行為。由于此處是第一次調用 Mock 對象,可以不必使用 reset() 方法。

          3 (b) expect() 是錄制 Mock 對象方法的調用,其參數就是 Mock 對象的方法,其中如果調用的方法有返回值,要通過 andReturn() 方法設置預期的返回值。

          4 (c) 處的 replay() 是結束錄制過程。 在調用 replay() 方法之前的狀態, EashMock 稱之為 “record 狀態 。該狀態下, Mock 對象不具備行為(即模擬接口的實現),它僅僅記錄方法的調用。在調用 replay() 后,它才以 Mock 對象預期的行為進行工作,檢查預期的方法調用是否真的完成。

          5 (d) 處的 verify() 是用于在錄制和回放兩個步驟完成之后進行預期和實際結果的檢查。這里就是檢查 accountDAOMock 是否如預期一樣調用了 getByNameAndPwd 方法。

          ?

          對于上面的舉例,它可能并不具有實際的價值,這里我只想拋磚引玉。在 N 層架構的 Java 程序中, Mock 對象在單元測試中正發揮著越來越重要的作用。我現在看到的是,在 Service 層與 Web 層, Mock 對象能很好的被應用。有人覺得在 Persistence 層也應該使用 Mock 對象,但就像我們所知道的,在使用 Hibernate 、 Ibatis ORM 工具的情況下,我們的 Persistence 層的測試主要測試的就是那些配置文件、查詢語句等(實際上是集成測試),如果還 Mock 的話,就失去了測試的意義。

          ???對于 Mock 的更多的信息,你可以訪問Mock Objects,在本文寫作的過程中,參考了使用模仿對象進行單元測試

          等文章,一并感謝。

          posted on 2007-04-26 08:35 kafka0102 閱讀(4053) 評論(1)  編輯  收藏 所屬分類: TDD

          評論

          # re: EasyMock使用手記 2009-05-07 15:04 josdoc
          Java開源文檔
          www.josdoc.com
          轉載了您的文章,若有異議請告知,謝謝!  回復  更多評論
            


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 石河子市| 札达县| 英德市| 萝北县| 西盟| 普兰店市| 仙游县| 浙江省| 夏邑县| 胶州市| 本溪| 正蓝旗| 汉沽区| 聂拉木县| 甘肃省| 城口县| 奉新县| 新民市| 深泽县| 莫力| 隆昌县| 武义县| 司法| 山阳县| 界首市| 周至县| 大洼县| 葵青区| 永吉县| 玉林市| 镇沅| 台安县| 炎陵县| 乐东| 霍林郭勒市| 荆州市| 禹州市| 五莲县| 祥云县| 册亨县| 化州市|