Mock method with Consumer

2019-08-05 09:49发布

问题:

I want to mock repository.actionOnFile(String path, Consumer<InputStream> action) in this source:

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

The problem is that the second argument is a lambda expression.

How to mock it with mockito, I need to pass to the accept method the input stream to test it?

回答1:

I found solution!

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


回答2:

If you only want to mock the Function argument then the following would work:

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


回答3:

How to mock it with mockito, I need to pass in accept method test input stream?

In your case, you want to test the getDocument() method.
So what you need to mock is the dependency of the class under test : that is the repository field.
actionOnFile.add() more specifically should be mocked.
According to your code, either the method should throw S3FileRepositoryException or it provokes a side effect not visible in the code.

In the exception scenario, you should write something as :

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

And in the successfull, you should just verify that the method is invoked :

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

Mocking a Consumer is really not a big deal.
It is a interface, you can mock any interface with Mockito.

The real issue is actually the Consumer makes not part of the API of the tested method.
It is a local variable.
Besides, it relies on an inputStream field that is not show in the code.
You cannot and have not to mock internal things.
Note that it also relies on a overloaded getDocument() method that is not mocked.
So you would need to provide a consistent InputStream if you want to getDocument() that accepts a inputStream doesn't throw an exception.

Long story short : I think that you should either rethink your design to extract the depending processings in another class or write an integration test.