Reputation: 620
I want to make the following work, but I don't know how to mock forEach behavior properly. (The code is taken from a related question Testing Java enhanced for behavior with Mockito )
@Test
public void aa() {
Collection<String> fruits;
Iterator<String> fruitIterator;
fruitIterator = mock(Iterator.class);
when(fruitIterator.hasNext()).thenReturn(true, true, true, false);
when(fruitIterator.next()).thenReturn("Apple")
.thenReturn("Banana").thenReturn("Pear");
fruits = mock(Collection.class);
when(fruits.iterator()).thenReturn(fruitIterator);
doCallRealMethod().when(fruits).forEach(any(Consumer.class));
// this doesn't work (it doesn't print anything)
fruits.forEach(f -> {
mockObject.someMethod(f);
});
// this works fine
/*
int iterations = 0;
for (String fruit : fruits) {
mockObject.someMethod(f);
}
*/
// I want to verify something like this
verify(mockObject, times(3)).someMethod(anyString());
}
Any help will be very appreciated.
Upvotes: 14
Views: 33494
Reputation: 404
You can use org.mockito.Mockito.doAnswer
as follows
Assume we are mocking a class that extends java.lang.Iterable
and we have created a Mockito mock for that class, and this mock is named iterable
and that we have created a collection called pages
which is the results you want the iterable class to return when the forEach method is returned (ie you want each invocation of forEach is to return the next element in the list pages
then you can program the iterable mock as follows:
doAnswer((Answer<Void>) invocation -> {
Object[] args = invocation.getArguments();
Consumer<Page<Alert>> consumer = (Consumer<Page<Alert>>) args[0];
pages.forEach(consumer);
return null;
}).when(iterable).forEach(any());
Upvotes: 1
Reputation: 219
Iterator mockIterator = mock(Iterator.class);
doCallRealMethod().when(fruits).forEach(any(Consumer.class));
when(fruits.iterator()).thenReturn(mockIterator);
when(mockIterator.hasNext()).thenReturn(true, false);
when(mockIterator.next()).thenReturn(mockObject);
Upvotes: 13
Reputation: 3036
The method forEach
of the Collection
interface is a "defender" method; it does not use Iterator
but call the Consumer
passed to the method.
If you are using Mockito version 2+ (*), you can ask the default method forEach
of the Collection
interface to be called:
Mockito.doCallRealMethod().when(fruits).forEach(Mockito.any(Consumer.class));
Note that the "defender" method is actually going to request an Iterator
to traverse the collection, hence will use the Iterator
you mocked in your test. It wouldn't work if no Iterator
was provided by the mocked collection, as with when(fruits.iterator()).thenReturn(fruitIterator)
(*): Mockito has added the possibility to support Java 8 default ("defender") method since version 2 - you can check the tracking issue here.
Upvotes: 9