用 mockito 模拟一个单身人士

新手上路,请多包涵

我需要测试一些遗留代码,它在 aa 方法调用中使用单例。测试的目的是确保类拆分器测试调用单例方法。我在 SO 上看到过类似的问题,但所有答案都需要其他依赖项(不同的测试框架)——不幸的是,我仅限于使用 Mockito 和 JUnit,但使用这种流行的框架,这应该是完全可能的。

单身人士:

 public class FormatterService {

    private static FormatterService INSTANCE;

    private FormatterService() {
    }

    public static FormatterService getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new FormatterService();
        }
        return INSTANCE;
    }

    public String formatTachoIcon() {
        return "URL";
    }

}

被测类:

 public class DriverSnapshotHandler {

    public String getImageURL() {
        return FormatterService.getInstance().formatTachoIcon();
    }

}

单元测试:

 public class TestDriverSnapshotHandler {

    private FormatterService formatter;

    @Before
    public void setUp() {

        formatter = mock(FormatterService.class);

        when(FormatterService.getInstance()).thenReturn(formatter);

        when(formatter.formatTachoIcon()).thenReturn("MockedURL");

    }

    @Test
    public void testFormatterServiceIsCalled() {

        DriverSnapshotHandler handler = new DriverSnapshotHandler();
        handler.getImageURL();

        verify(formatter, atLeastOnce()).formatTachoIcon();

    }

}

这个想法是配置可怕的单例的预期行为,因为被测试的类将调用它的 getInstance 然后是 formatTachoIcon 方法。不幸的是,这失败并显示错误消息:

 when() requires an argument which has to be 'a method call on a mock'.

原文由 fbielejec 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 430
2 个回答

您所要求的是不可能的,因为您的遗留代码依赖于静态方法 getInstance() 并且 Mockito 不允许模拟静态方法,因此以下行将不起作用

when(FormatterService.getInstance()).thenReturn(formatter);

有两种方法可以解决此问题:

  1. 使用允许模拟静态方法的不同模拟工具,例如 PowerMock。

  2. 重构您的代码,以便您不依赖静态方法。我能想到的实现这一点的侵入性最小的方法是向 DriverSnapshotHandler 添加一个构造函数,它会注入 FormatterService 依赖项。此构造函数将仅用于测试,您的生产代码将继续使用真正的单例实例。

    public static class DriverSnapshotHandler {

       private final FormatterService formatter;

       //used in production code
       public DriverSnapshotHandler() {
           this(FormatterService.getInstance());
       }

       //used for tests
       DriverSnapshotHandler(FormatterService formatter) {
           this.formatter = formatter;
       }

       public String getImageURL() {
           return formatter.formatTachoIcon();
       }
   }

然后,您的测试应如下所示:

 FormatterService formatter = mock(FormatterService.class);
when(formatter.formatTachoIcon()).thenReturn("MockedURL");
DriverSnapshotHandler handler = new DriverSnapshotHandler(formatter);
handler.getImageURL();
verify(formatter, atLeastOnce()).formatTachoIcon();

原文由 noscreenname 发布,翻译遵循 CC BY-SA 3.0 许可协议

我认为这是可能的。查看 如何测试单例 的示例

测试前:

 @Before
public void setUp() {
    formatter = mock(FormatterService.class);
    setMock(formatter);
    when(formatter.formatTachoIcon()).thenReturn(MOCKED_URL);
}

private void setMock(FormatterService mock) {
    try {
        Field instance = FormatterService.class.getDeclaredField("instance");
        instance.setAccessible(true);
        instance.set(instance, mock);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

测试后 - 清理类很重要,因为其他测试会与模拟实例混淆。

 @After
public void resetSingleton() throws Exception {
   Field instance = FormatterService.class.getDeclaredField("instance");
   instance.setAccessible(true);
   instance.set(null, null);
}

考试:

 @Test
public void testFormatterServiceIsCalled() {
    DriverSnapshotHandler handler = new DriverSnapshotHandler();
    String url = handler.getImageURL();

    verify(formatter, atLeastOnce()).formatTachoIcon();
    assertEquals(MOCKED_URL, url);
}

原文由 Kyrylo Semenko 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题