2012-12-28 212 views
10

我需要使用Mockito測試handleIn()方法。Mockito旁路靜態測試方法

但是代碼需要調用這個遺留代碼Util.getContextPDO這是一個靜態方法。

請注意,在測試環境中,此Util.getContextPDO總是返回Exception,並且我打算通過總是返回一個虛擬IPDO來繞過此Util.getContextPDO()。

public class MyClass { 
    public IPDO getIPDO() 
    { 
    return Util.getContextPDO(); // note that Util.getContextPDO() is a static, not mockable. 
    } 

    public String handleIn(Object input) throws Throwable 
    { 
    String result = ""; 
    IPDO pdo = getIPDO(); 

    // some important business logic. 

    return result; 
    } 
} 

起初我以爲這achieveable使用間諜()類「MyClass的」,所以我可以嘲笑getIPDO的)的返回值(。下面是使用間諜()我最初的努力

@Test 
public void testHandleIn() throws Exception 
{ 
    IPDO pdo = new PDODummy(); 


    MyClass handler = new MyClass(); 
    MyClass handler2 = spy(handler); 

    when(handler2.getIPDO()).thenReturn(pdo); 
    PDOUtil.setPDO(pdo, LogicalFieldEnum.P_TX_CTGY, "test123"); 
    IPDO pdoNew = handler2.getIPDO(); 

    Assert.assertEquals("test123,(PDOUtil.getValueAsString(pdoNew, LogicalFieldEnum.P_TX_CTGY))); 

} 

然而時(handler2.getIPDO())thenReturn(PDO)。拋出我想避免的異常(因爲handler2.getIPDO())似乎稱爲真正的方法。

關於如何測試這部分代碼的任何想法?

+0

我們將** PowerMock **與** Mockito **一起使用,帶來傳統。這變得如此簡單,所以我們制定了規則,以避免** PowerMock **與傳統更有效的戰鬥 –

+1

PowerMock的「唯一」問題是,你不得不使用自己的測試運行器,這並不總是一個選項(當寫Android Robolectric測試,例如,需要他們自己的robolectric轉輪) –

回答

10

改變了我的測試:

@Test 
public void testHandleIn() throws Exception 
{ 
    IPDO pdo = new PDODummy(); 


    MyClass handler = new MyClass(); 
    MyClass handler2 = spy(handler); 

    doReturn(pdo).when(handler2).getIPDO(); 
    PDOUtil.setPDO(pdo, LogicalFieldEnum.P_TX_CTGY, "test123"); 
    IPDO pdoNew = handler2.getIPDO(); 

    Assert.assertEquals("test123,(PDOUtil.getValueAsString(pdoNew, LogicalFieldEnum.P_TX_CTGY))); 

} 

解決閱讀Effective Mockito後。

+2

間諜的「問題」,即實際方法在存根期間被調用。避免這種情況的唯一方法是使用doXXXX.when(mock).somemeod(anyParam())表單。 –

11

在第三方API中擺脫靜態調用的一個好方法是在接口後面隱藏靜態調用。

比方說,你做這個接口:

interface IPDOFacade { 

    IPDO getContextPDO(); 
} 

,並有一個簡單的呼籲第三方API靜態方法的默認實現:

class IPDOFacadeImpl implements IPDOFacade { 

    @Override 
    public IPDO getContextPDO() { 
     return Util.getContextPDO(); 
    } 
} 

然後,它僅僅是注入的問題將接口依賴於MyClass並使用該接口,而不是直接使用第三方API:

public class MyClass { 

    private final IPDOFacade ipdoFacade; 

    public MyClass(IPDOFacade ipdoFacade) { 
     this.ipdoFacade = ipdoFacade; 
    } 

    public String handleIn(Object input) throws Throwable 
    { 
     String result = ""; 
     IPDO pdo = getIPDO(); 

     someImportantBusinessLogic(pdo); 

     return result; 
    } 

    ... 

} 

在您的單元測試中,您可以輕鬆地模擬您自己的界面,以任何您喜歡的方式對其進行存根並將其注入被測單元。

  • 避免了需要使私有方法包私有。
  • 通過避免部分嘲諷使您的測試更具可讀性。
  • 適用控制反轉。
  • 將您的應用程序從特定第三方庫中分離出來。
+0

現在,您如何爲IPDOFacadeImpl.getContextPDO()編寫單元測試? – aquacode

+0

你不這樣做,你爲它寫一個集成測試。 – bowmore

+0

很好的答案。這是更多的代碼/文件,但感覺不錯。它允許你避免間諜(Mockito說要避免),允許你避免在實例對象上調用靜態方法的警告,並遵守IoC。 – Marquee

1
when(handler2.getIPDO()).thenReturn(pdo); 

將實際調用該方法,然後返回pdo不管。

鑑於:

doReturn(pdo).when(handler2).getIPDO(); 

將返回PDO不調用getIPDO()方法。

+0

仍與doReturn和靜態方法將被調用時。 – Dish