2012-12-28 119 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