首页 文章

Mockito绕过静态方法进行测试

提问于
浏览
11

我需要使用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;
  } 
}

最初我认为这可以通过使用类“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)));

}

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

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

3 回答

  • 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;
        }
    
        ...
    
    }
    

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

    这个

    • 避免了将私有方法包私有化的需要 .

    • 通过避免部分模拟使您的测试更具可读性 .

    • 适用控制反转 .

    • 将您的应用程序与特定的第三方库分离 .

  • 1

    将我的测试更改为:

    @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后解决了 .

  • 10
    when(handler2.getIPDO()).thenReturn(pdo);
    

    实际上会调用该方法然后返回 pdo 无论如何 .

    鉴于:

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

    将返回pdo而不调用 getIPDO() 方法 .

相关问题