当前位置: 首页 > 知识库问答 >
问题:

用mockito嘲弄一个单身的人

盖翰池
2023-03-14

我需要测试一些遗留代码,这些代码在a方法调用中使用了单例。测试的目的是确保类sunder测试对singletons方法进行调用。我在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'.

共有3个答案

景辰钊
2023-03-14

您的getInstance方法是静态的,因此不能使用mockito进行嘲弄。http://cube-drone.com/media/optimized/172.png。您可能需要使用PowerMockito来执行此操作。虽然我不建议这样做。我将通过依赖注入测试DriverSnapshotHandler:

public class DriverSnapshotHandler {

    private FormatterService formatterService;

    public DriverSnapshotHandler(FormatterService formatterService) {
        this.formatterService = formatterService;
    }

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

}

单元测试:

public class TestDriverSnapshotHandler {

    private FormatterService formatter;

    @Before
    public void setUp() {

        formatter = mock(FormatterService.class);

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

    }

    @Test
    public void testFormatterServiceIsCalled() {

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

        verify(formatter, times(1)).formatTachoIcon();

    }

}

您可能希望在@after方法中将mock设置为null。这是IMHO的清洁解决方案。

伍胡媚
2023-03-14

我认为这是可能的。请参阅如何测试单例的示例

测试前:

@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);
}
戎志勇
2023-03-14

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

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

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

>

  • 使用允许模拟静态方法的其他模拟工具,如PowerMock。

    重构您的代码,这样您就不会依赖于静态方法。我能想到的实现这一点的最小侵入性的方法是向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();
    

  •  类似资料:
    • 我正在为类编写一个单元测试,该类如下所示: 我想编写一个简单的单元测试,它将方法存根(这样它就不会实际触发并命中数据库),但它允许我验证调用是否最终执行。Mockito似乎是这份工作的合适工具。 这似乎是一个很好的游戏计划(至少对我来说)。但当我实际编写代码时,在测试方法的第2行(行)出现以下编译器错误: 类型Mockito中的(T)不适用于参数(void)时的方法 我看到Mockito无法模拟返

    • 在helper类的静态方法中调用时,它会抛出一个NPE。我所做的是嘲笑MarkupMaker和它的返回值(一个Markup实例)。最后,我希望调用标记实例的。无论我做什么-的调用都是抛出一个NPE。我找不到任何文档告诉我如何在spock中详细模拟方法调用值。 编辑:我添加了示例。的调用返回null,即使我在spock测试中对其进行了嘲弄。 test.groovy java(执行模拟对象的方法) j

    • 正在测试的类: 下面是测试代码。我模拟了测试中的类class,并覆盖了方法getFruits的返回值。但是当我运行mock时,我没有得到预期的mock返回值。Easymock可以将返回值替换为被测试类的方法,如果这些方法是显式模拟的。当我模拟真实的对象方法时,如何获得模拟的返回值。

    • 我必须测试一个用Spring注入两个映射的类。我想模拟两张地图。我使用模拟符号如下: 但我得到以下错误:

    • 我正在尝试Mockito进行模拟和单元测试。 试图使用模拟autowired bean。但bean在运行时为空。 正在测试的类。 测试类: 错误: 我调试过,是因为(使用MockBean进行模拟,因为它是。) 这个代码有什么问题?

    • 任何帮助都将得到高度赞赏。 提前致谢