Sky's blog

          我和我追逐的夢(mèng)

          常用鏈接

          統(tǒng)計(jì)

          其他鏈接

          友情鏈接

          最新評(píng)論

          easymock教程-partial class mocking

              easymock中提供對(duì)于類的mock功能,我們可以方便的mock這個(gè)類的某些方法,指定預(yù)期的行為以便測(cè)試這個(gè)類的調(diào)用者。這種場(chǎng)景下被mock的類在測(cè)試案例中扮演的是次要測(cè)試對(duì)象或者說依賴的角色,主要測(cè)試對(duì)象是這個(gè)mock類的調(diào)用者。但是有時(shí)候我們需要將這個(gè)測(cè)試類作為主要測(cè)試對(duì)象,我們希望這個(gè)類中的部分(通常是大部分)方法保持原有的正常行為,只有個(gè)別方法被我們mock掉以便測(cè)試。

          1. 使用方法

              我們先來看看這個(gè)partial class mocking 是如何工作的:

              public class Service {

                  
          public void execute() {
                      actualMethod();
                      needMockMethod();
                  }


                  
          void actualMethod() {
                      System.out.println(
          "call actualMethod()");
                  }


                  
          public void needMockMethod() {
                      System.out.println(
          "call needMockMethod()");
                  }


              }

              我們給出了一個(gè)非常簡(jiǎn)單的類,我們將要測(cè)試execute()方法,期望能測(cè)試到actualMethod()這個(gè)方法的正常行為,然后需要mock掉needMockMethod().

          public class PartialClassMockTest extends Assert {

              @Test
              
          public void testPartialMock() {
                  Service service 
          = EasyMock.createMockBuilder(Service.class).addMockedMethod("needMockMethod").createMock();
                  service.needMockMethod();
                  EasyMock.expectLastCall();

                  EasyMock.replay(service);
                  service.execute();
                  EasyMock.verify(service);
              }

          }

              上面的測(cè)試案例運(yùn)行通過,輸出為"call actualMethod()",沒有"call needMockMethod()",說明我們?cè)O(shè)置的mock生效了。我們創(chuàng)建的mock類的確是只有部分我們制定的方法是mock的,其他都是正常行為。

              再來看看為什么我們要需要partial class mocking 這個(gè)功能?為什么需要mock掉其中的一個(gè)方法?

              我們來看看下面這個(gè)更加真實(shí)的例子:

             public class Service {

                  
          public String execute2() {
                      
          return getConfiguration();
                  }


                  
          public String getConfiguration() {
                      
          return Configuration.getUsername();
                  }

              }


              
          public class Configuration {
                  
          public static String getUsername() {
                      
          //ignore the code to get configuration from file or database
                      return "username";
                  }

              }

              這里例子中,需要測(cè)試的 execute2()方法需要調(diào)用getConfiguration()方法,而getConfiguration()方法則調(diào)用了Configuration的靜態(tài)方法來獲取配置信息。我們假設(shè)讀取配置的代碼比較復(fù)雜不能直接在單元測(cè)試環(huán)境下運(yùn)行,因此通過情況下這里的execute2()方法就會(huì)因?yàn)檫@個(gè)getConfiguration()而造成無法測(cè)試。因此我們可以考慮通過partial class mocking的功能來mock掉getConfiguration()方法從而使得我們的測(cè)試案例可以覆蓋到execute2()方法

              @Test
              
          public void testStaticMethod() {
                  Service service 
          = EasyMock.createMockBuilder(Service.class).addMockedMethod("getConfiguration").createMock();
                  EasyMock.expect(service.getConfiguration()).andReturn(
          "abc");

                  EasyMock.replay(service);
                  assertEquals(
          "abc", service.execute2());
                  EasyMock.verify(service);
              }

              這個(gè)測(cè)試案例可以正常通過,我們通過partial class mocking成功的避開了getConfiguration()這個(gè)絆腳石。

              當(dāng)然這里的實(shí)例代碼本身就有點(diǎn)問題,應(yīng)該采用DI的方法將configuration注入進(jìn)來,而不是在內(nèi)部通過靜態(tài)方法來獲取。因此一個(gè)建議是在使用partial class mocking功能前,先看看是不是可以通過重構(gòu)來顯改進(jìn)測(cè)試類。只有當(dāng)我們有足夠充分的不得已的理由時(shí),才使用partial class mocking這種變通(或者說取巧)的方式來解決問題。

             
          2. 限制

              上面兩個(gè)例子中,我們仔細(xì)看看會(huì)發(fā)現(xiàn),被mock的方法都是public的。我們?cè)囍鴮⒎椒ㄐ薷臑閜rotected和default,partial class mocking依然生效。但是修改為private之后,則拋出異常:

          java.lang.IllegalArgumentException: Method not found (or private): needMockMethod
           at org.easymock.internal.MockBuilder.addMockedMethod(MockBuilder.java:75)
           at net.sourcesky.study.easymock.tutorial.PartialClassMockTest.testPartialMock(PartialClassMockTest.java:52)

              或者將mock的方法繼續(xù)保持public,但是加上final,則拋出以下異常:

          java.lang.IllegalStateException: no last call on a mock available
           at org.easymock.EasyMock.getControlForLastCall(EasyMock.java:521)
           at org.easymock.EasyMock.expectLastCall(EasyMock.java:512)
           at net.sourcesky.study.easymock.tutorial.PartialClassMockTest.testPartialMock(PartialClassMockTest.java:54)

              我們回到之前的章節(jié),class mocking里面講述了class mocking的一些限制:private方法和final方法是不能mock的。partial class mocking下這些限制依然存在。因此,為了開啟partial class mocking,我們不得不稍微破壞一下類的封裝原則,對(duì)于原本應(yīng)該是private的方法,修改為protected或者default。

              不得不再次申明,partial class mocking不是一個(gè)足夠好的解決方案,它只適合在不得已的情況下使用,不要太依賴這個(gè)特性。重構(gòu)代碼改善代碼才是王道。

          3. 疑問

              另外class mocking中還講到,對(duì)于類的equals(), toString()和hashCode()這三個(gè)方法,class mocking下是easymock為這三個(gè)方法內(nèi)建了easymock的實(shí)現(xiàn),因此也不能mock。而partial class mocking,這三個(gè)方法同樣不能mock,但是easymock不再為它們內(nèi)建實(shí)現(xiàn),而是使用它們正常的功能。

              關(guān)于這點(diǎn)還是有一點(diǎn)疑問,我在easymock的官方文檔中看到以下描述

          Remark: EasyMock provides a default behavior for Object's methods (equals, hashCode, toString). However, for a partial mock, if these methods are not mocked explicitly, they will have their normal behavior instead of EasyMock default's one.

              言下之意,似乎equals, hashCode, toString這三個(gè)方法還是可以顯式mock的。但是我測(cè)試了一下:

              public class Service {

                  
          public String execute3() {
                      actualMethod();
                      
          return toString();
                  }


                  @Override
                  
          public String toString() {
                      
          return "defaultToString()";
                  }

              }


              @Test
              
          public void testToStringMethod() {
                  Service service 
          = EasyMock.createMockBuilder(Service.class).addMockedMethod("toString").createMock();
                  EasyMock.expect(service.toString()).andReturn(
          "abc");
                  EasyMock.replay(service);
                  assertEquals(
          "abc", service.execute3());
                  EasyMock.verify(service);
              }

              toString()方法的mock沒能生效,拋出異常:

          java.lang.IllegalStateException: no last call on a mock available
           at org.easymock.EasyMock.getControlForLastCall(EasyMock.java:521)
           at org.easymock.EasyMock.expect(EasyMock.java:499)
           at net.sourcesky.study.easymock.tutorial.PartialClassMockTest.testToStringMethod(PartialClassMockTest.java:74)

              可以看到明顯是EasyMock.expect(service.toString()).andReturn("abc"); 這里的record沒有成功。

          posted on 2010-11-30 14:23 sky ao 閱讀(3111) 評(píng)論(0)  編輯  收藏 所屬分類: software test

          主站蜘蛛池模板: 和平县| 莎车县| 六安市| 大方县| 广元市| 莱西市| 稷山县| 龙山县| 闻喜县| 登封市| 白玉县| 和静县| 彰化市| 龙江县| 海盐县| 肥东县| 永定县| 漠河县| 洮南市| 葫芦岛市| 广安市| 永福县| 白玉县| 嘉祥县| 文成县| 莎车县| 中江县| 光泽县| 中宁县| 来安县| 屏东市| 永修县| 石河子市| 邵阳市| 镇原县| 西城区| 通许县| 万盛区| 长顺县| 阜新| 乐都县|