Mockito绕过静态方法进行测试

Rud*_*udy 12 java junit mockito

我需要使用Mockito测试handleIn()方法.

但是代码需要调用这个遗留代码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;
  } 
}
Run Code Online (Sandbox Code Playgroud)

最初我认为这可以通过使用类"MyClass"的spy()来实现,所以我可以模拟getIPDO()的返回值.以下是我使用spy()的初步努力

@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)));

}
Run Code Online (Sandbox Code Playgroud)

但是when(handler2.getIPDO()).thenReturn(pdo); 抛出我想要避免的异常(因为handler2.getIPDO())似乎调用了真正的方法.

关于如何测试这部分代码的任何想法?

Rud*_*udy 11

将我的测试更改为:

@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)));

}
Run Code Online (Sandbox Code Playgroud)

阅读完有效Mockito后解决.

  • 间谍的"问题",即在存根期间调用实际方法.避免这种情况的唯一方法是使用doXXXX.when(mock).somemethod(anyParam())表单. (2认同)

bow*_*ore 11

在第三方API上摆脱静态调用的一种好方法是隐藏接口后面的静态调用.

假设您创建此界面:

interface IPDOFacade {

    IPDO getContextPDO();
}
Run Code Online (Sandbox Code Playgroud)

并有一个默认实现,只需调用第三方API上的静态方法:

class IPDOFacadeImpl implements IPDOFacade {

    @Override
    public IPDO getContextPDO() {
        return Util.getContextPDO();
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,只需将接口依赖注入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;
    }

    ...

}
Run Code Online (Sandbox Code Playgroud)

在单元测试中,您可以轻松地模拟自己的界面,以任何您喜欢的方式存根,并将其注入被测单元.

这个

  • 避免需要将私有方法包私有化.
  • 通过避免部分模拟,使您的测试更具可读性.
  • 适用于控制反转.
  • 将您的应用程序与特定的第三方库分离.