I have a method signature like
public void add(byte[] key, Optional<Byte[]> secondaryKey) { ... }
My test looks something like
byte[] key = "testKey".getBytes();
byte[] secondaryKey = "secondaryKey".getBytes()
//call the method that internally calls add()
Mockito.verify(mockClass).add(key, Optional.of(ArrayUtils.toObject(secondaryKey))
The verification always fails in this case saying wanted parameters are different from actual. I have a similar add method that just take byte[] key as input parameter. Test on that method succeeds. So I think there is something wrong with the way I am trying to match Optional parameter here.
The Optional does not perform a deepEquals, therefore the equality check will fail considering that you have passed an Byte[] into the Optional.
You can see this bug report from a user that faced a similar issue. JDK-8075723
You will probably want to leverage some ArgumentMatchers to compare the arguments passed into your mock.
Since you have an Optional being passed through, you can unwrap that object using ArgumentMatchers.argThat which requires you to implement a ArgumentMatcher.matches method.
Mockito.verify(mockClass).add(ArgumentMatchers.eq(key), ArgumentMatchers.argThat(r -> {
return r.isPresent() && Objects.deepEquals(secondaryKey, r.get())
));
Edit:
You can also use ArgumentCaptor if you prefer to capture the state of the parameters passed into the mocks and perform assertions.
ArgumentCaptor<Optional> captor =ArgumentCaptor.forClass(Optional.class)
verify(pojo).add(eq(key), captor.capture());
Optional<byte[]> result = captor.getValue();
assertTrue(result.isPresent());
assertArrayEquals(secondaryKey, result.get());
Related
I have the following code which I want to test:
pojo.setStatus(Status.INITIAL);
pojo.setExecutionCounter(0);
//eventRepository is a mock
scheduleEvent(this.eventRepository.save(pojo).get());
In a unit test, I'd like to verify that
the pojo has been modified accordingly
The event which will be scheduled has status INITIAL and the counter is 0
save of the mock has been called
Unfortunately, I have no clue how to do that, since I have to return the argument which I have to capture. Maybe it's getting more clear when I show the example of my unit test:
succeededEvent.setEventStatus(INITIAL);
succeededEvent.setExecutionCounter(0);
//Actually we want here to return the argument
//which is captured by eventArgumentCaptor??
when(this.eventRepository.save(this.eventArgumentCaptor.capture()))
.thenReturn(succededEvent);
this.processor.processEvent(initialEvent);
Mockito.verify(this.eventRepository,
Mockito.times(1)).save(eventCaptureExecuteCaptor.capture());
final Event capturedEvent = eventCaptureExecuteCaptor.getValue();
//Counter and status should be reset
assertEquals(new Integer(0), capturedEvent.getExecutionCounter());
assertEquals(INITIAL, capturedEvent.getEventStatus());
verify(this.eventRepository,
times(1)).save(eq(eventCaptureExecuteCaptor.getValue()));
Not sure why you need to use captor while stubbing.
You use it, as designed, after the invocation of the SUT:
this.processor.processEvent(initialEvent);
Mockito.verify(this.eventRepository,
Mockito.times(1)).save(eventCaptureExecuteCaptor.capture());
While stubbing, you can directly go for the concrete object that is expected:
when(this.eventRepository.save(succededEvent)
.thenReturn(succededEvent);
or use a generic input if you do not have that object at hand on set-up:
when(this.eventRepository.save(anyClass(EventPojo.class))
.thenReturn(succededEvent);
Edit:
You can also use the thenAnswer along with accepting any input class of Pojo type:
when(this.eventRepository.save(Mockito.any(EventPojo.class))
.thenAnswer(new Answer<EventPojo>(){
EventPojo pojo = invocation.getArgument(0);
return pojo;
}
);
as this is an anonymous implementation, you can catch the state of the passed pojo if you have the need.
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?
I found solution!
doAnswer(ans -> {
Consumer<InputStream> callback = ans.getArgument(1, Consumer.class);
InputStream stream = new ByteArrayInputStream("test".getBytes(StandardCharsets.UTF_8));
callback.accept(stream);
return null;
}).when(repository).actionOnFile(eq("any"), any(Consumer.class));
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[...]
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.
I have issue where when I use Java parallelStream instead of stream my tests fail. This happens because I am returning Mock objects in a strict order rather than controlling Mock objects returned based on input.
The following is my current code used to return mocks objects:
when(myOperation.getSomething(any(String.class)))
.thenAnswer(AdditionalAnswers.returnsElementsOf(aListOfThings)));
How can I concisely control the return value based off the argument I am passing to "getSomething"?
You can do something like that:
when(myOperation.getSomething(any(String.class))).thenAnswer(new Answer<SomeThing>() {
#Override
public SomeThing answer(final InvocationOnMock invocation) throws Throwable {
// HERE ====> use invocation.getArguments()
return new SomeThing();
}
});
Here the answer return a SomeThing instance, you will need to adjust to your needs
Some reading:
Java 8 custom answer support
doAnswer documentation
InvocationOnMock javadoc
Instead of using an Answer, you can just iterate across your values and set up specific stubs for each one. Unlike with an Answer, you have to be able to predict all values for which you're stubbed, but for your particular case here it sounds like that might not be a problem.
for (int i = 0; i < aListOfThings.size(); i++) {
when(myOperation.getSomething(aListOfKeys.get(i)))
.thenReturn(aListOfThings.get(i));
}
I want to use mockito spy.
When I set a return value in both following ways:
when(imagesSorterSpy.sortImages(imagesAsInsertionOrder, user)).thenReturn(imagesSorterSpy.sortImages(imagesAsInsertionOrder, user, fakeNowDate));
doReturn(imagesSorterSpy.sortImages(imagesAsInsertionOrder, user, fakeNowDate)).when(imagesSorterSpy).sortImages(imagesAsInsertionOrder, user);
I see the return value is being evaluated eagerly
meaning when this "setting" line is executed.
how can i force the spy to evaluate the return value only on demand?
meaning when the "when" condition is met.
update
Thanks to #RobbyCornelissen I have tried this code:
when(imagesSorterSpy.sortImages(imagesAsInsertionOrder, user)).thenAnswer(new Answer() {
public Object answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
ImagesSorter mock = (ImagesSorter)invocation.getMock();
return mock.sortImages((List<Image>)args[0], (UserInfo)args[1], fakeNowDate);
}
});
But it didn't help:
1) the "when" expression was invoked immediately. (not wanted)
2) eventually the callback wasn't call.
First let me warn you on partial mocks, because that is what the code is actually doing, it's wrong design wise. It may be more relevant to use a strategy pattern to compose behavior of the tested subject. Mockito team (including me) strongly advises to stay away of partial mocks whenever possible.
EDIT : I don't know the code and I don't know exactly which component under test but from what I gather there's a type responsible to sort images, let's call it ImagesSorter.
So first case ImagesSorter is a dependency of a test subject, so in this case just stubbing the mock of ImagesSorter will do.
If however it is ImagesSorter itself under test, and stubbing a special method of this class is called a partial mock and it is plain wrong. It exposes internal of the production code in the test. So there's several solutions.
As the code snippet showed in the answer shows a fakeDate, one of the solution is to not use things like new Date() and code a simple class TimeSource whose sole responsibility is to provide a date. And in tests the bwhavior of this TimeSOurce could be overriden.
A simplier solution would be to use JodaTime as it provides this functionality built in.
If the scope of test goes beyond changing the date, then maybe ImagesSorter needs a way to be configured with other objects. Inspiration on how to do it can be found with the cache builder of guava. If the configuration is dead simple then a simple constructor coud do it.
That could look like :
class ImagesSorter {
ImagesSorterAlso algo;
ImagesSorter(ImagesSorterAlgo algo) { this.algo = algo; }
Iterable sortImages(...) {
algo.sort(...);
}
}
interface ImagesSorterAlgo {
Iterable sort(...);
}
Now about your questions :
1) the "when" expression was invoked immediately. (not wanted)
It is expected imagesSorterSpy is a spy so by default it calls the real code. Instead you should use the alternate API, the same that #RobbyCornelissen showed. i.e.
doAnswer(sortWithFakeDate()).when(imagesSorterSpy).sortImages(imagesAsInsertionOrder, user);
// with BDD aliases (BDDMockito) which I personnaly finds better
willAnswer(sortWithFakeDate()).given(imagesSorterSpy).sortImages(imagesAsInsertionOrder, user);
will(sortWithFakeDate()).given(imagesSorterSpy).sortImages(imagesAsInsertionOrder, user);
sortWithFakeDate() would a static factory method that returns the answer, so the code reads well, and maybe reused elsewhere.
2) eventually the callback wasn't call.
This issue is most probably due to non equal arguments. You may need to check the equals method. Or relax the stub using the any() matcher.
I don't know the types of the arguments and classes you're using, so I can't provide a complete example, but you can stub using callbacks with the Answer<T> interface:
Mockito.doAnswer(new Answer() {
Object answer(InvocationOnMock invocation) {
ImagesSorter mock = (ImagesSorter) invocation.getMock();
Object[] args = invocation.getArguments();
return mock.sortImages((List<Image>) args[0], (UserInfo) args[1],
fakeNowDate);
}
}).when(imagesSorterSpy).sortImages(imagesAsInsertionOrder, user);
I am attempting to verify that a method was called a number of times using the Mockito verify functionality. The problem I am encountering however is that the method is overloaded so it claims that the method was not called. To Add a wrench into the mix, I also wish to capture what the argument was that was passed to this method. Here is what I have so far:
#Test
public void simpleTest() throws IOException {
FlumeAppender mockAppender = Mockito.mock(FlumeAppender.class);
ArgumentCaptor<LoggingEvent> arguments = ArgumentCaptor.forClass(LoggingEvent.class);
// Load the message that should be sent to the class being tested
InputStream in = this.getClass().getResourceAsStream("/testMessage.xml");
StringWriter writer = new StringWriter();
IOUtils.copy(in, writer, "UTF-8");
String testMessage = writer.toString();
// Send a message to the class being tested. This class will
// (hopefully) call the function I am listening to below
eventSubscriber.handleMessage(testMessage);
// Verify that the append method was called twice
Mockito.verify(mockAppender, Mockito.times(2)).append(
arguments.capture());
// Do something with the arguments
}
Like I said, the function I am attempting to verify (append) is overloaded. Is it possible to specify which append function I am asking to verify while still capturing the arguments?
Embarrassingly, I have discovered the solution to my problem was the result of a simple error. I will post the answer for reference.
When creating the ArgumentCaptor, specify the argument type you are expecting using generics. I had done this, but sadly I used the type from one of the other versions of the method that I was not expecting to be called. Simple mistake.
// This declaration
ArgumentCaptor<LoggingEvent> arguments = ArgumentCaptor.forClass(LoggingEvent.class);
// Should have been:
ArgumentCaptor<Event> arguments = ArgumentCaptor.forClass(Event.class);
Once this is correct the verify function should work as expected, using the type from the ArgumentCaptor to determine which method to look at.
So close...