【问题标题】:Mock method with Consumer使用消费者的模拟方法
【发布时间】:2018-04-23 18:22:31
【问题描述】:

我想在这个来源中模拟repository.actionOnFile(String path, Consumer<InputStream> action)

@Autowired
private FileRepositoryService repository;

public Document getDocument(URL url) {
    MutableObject<Document> obj = new MutableObject<>();
    Consumer<InputStream> actionOnFile = inputStream -> obj.setValue(getDocument(inputStream));
    try {
        repository.actionOnFile(url.toExternalForm(), actionOnFile);
    } catch (S3FileRepositoryException e) {
        throw e.getCause();
    }
    return obj.getValue();
}

问题在于第二个参数是一个 lambda 表达式。

如何用mockito模拟它,我需要将输入流传递给accept方法来测试它吗?

【问题讨论】:

  • 您可能希望重构您的服务以接受Function&lt;InputStream, R&gt; 并返回R。我假设您成为消费者之后关闭输入流,但您的问题正是当您的服务依赖某些副作用来产生任何结果时会发生什么。

标签: java lambda mocking


【解决方案1】:

我找到了解决办法!

doAnswer(ans -> {
    Consumer<InputStream> callback = (Consumer<InputStream>) ans.getArguments()[1];
    InputStream stream = new ByteArrayInputStream(
            "test".getBytes(StandardCharsets.UTF_8.name()));
    callback.accept(stream);
    return null;
}).when(repository).actionOnFile(eq("any"), any(Consumer.class));

【讨论】:

  • 我认为您应该接受自己的答案。虽然它并不漂亮,但您还需要测试您在 getDocument 方法中内联定义的 actionOnFile 使用者
  • 请推荐更漂亮的解决方案)
  • 对不起,我也不知道。用 Mockito 写这个并不是一件很自然的事情。在我的项目中,我将所有杂乱的代码移到了一个单独的函数中,并给它起了一个漂亮的名字:)
【解决方案2】:

如何用mockito模拟它,我需要通过accept方法测试 输入流?

在您的情况下,您想测试 getDocument() 方法。
所以你需要模拟的是被测类的依赖关系: 那是 repository 字段。
actionOnFile.add() 更具体地说应该被嘲笑。
根据您的代码,该方法应该抛出 S3FileRepositoryException 或引发代码中不可见的副作用。

在异常情况下,你应该写成:

 Mockito.when(fileRepositoryServiceMock.actionOnFile(url.toExternalForm(), actionOnFile)).thenThrow(new S3FileRepositoryException(cause));

在成功的情况下,您应该只验证该方法是否被调用:

 Mockito.verify(fileRepositoryServiceMock).actionOnFile(url.toExternalForm(), actionOnFile));

嘲笑Consumer 真的没什么大不了的。
它是一个接口,你可以用 Mockito 模拟任何接口。

真正的问题实际上是Consumer 不属于测试方法的API。
它是一个局部变量。
此外,它依赖于代码中未显示的inputStream 字段。
您不能也不必模拟内部事物。
请注意,它还依赖于未模拟的重载 getDocument() 方法。
因此,如果您想要接受 inputStream 不会引发异常的getDocument(),则需要提供一致的 InputStream

长话短说:我认为您应该重新考虑您的设计以提取另一个类中的依赖处理或编写集成测试。

【讨论】:

    【解决方案3】:

    如果您只想模拟 Function 参数,则以下方法可行:

    Mockito.when(convertStringtoInt(Mockito.any(String.class), Mockito.any(Consumer.class))).then[...]
    

    【讨论】:

      最近更新 更多