How to mock forEach behavior with Mockito
Asked Answered
V

3

13

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.

Vellum answered 21/3, 2018 at 12:12 Comment(5)
I think your question gets answered here: #6379808Granary
mocking iterator() is fine. I think mocking iterator() and mocking forEach() is different thing since forEach() doesn't call iterator().Vellum
I changed the example code to make it more clear.Vellum
What exactly are you trying to test? I expect that you ultimately want to confirm that something happens to each element of the fruits collection. In this example, it's just printing to the console but your actual application may be more complex. Is that right? I can post an answer with suggestions with just a little more information.Ferial
Hi, thank you for the comment. I edited the question to make it more clear. I basically want to verify that some method is called properly in the forEach loop.Vellum
G
13
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);
Gangrene answered 13/6, 2019 at 8:9 Comment(0)
C
9

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.

Celebes answered 21/3, 2018 at 13:23 Comment(3)
Hi Alexandre, I tried with "doCallRealMethod" but, it doesn't work. I edited the sample code to have doCallRealMethod. Is this what you are saying ? or am I missing something ?Vellum
It seems my intellij issue. I can make it work with your suggestion from command-line. thank you.Vellum
Any Kotlin version?Berck
K
0

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());
Kort answered 22/5 at 10:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.