Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mock forEach behavior with Mockito

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.

like image 896
feeblefakie Avatar asked Mar 21 '18 12:03

feeblefakie


People also ask

How to mock methods using Mockito?

With Mockito, you create a mock, tell Mockito what to do when specific methods are called on it, and then use the mock instance in your test instead of the real thing. After the test, you can query the mock to see what specific methods were called or check the side effects in the form of changed state.

How can specify mock behaviour of an object?

When you create a mock, you create an associated behavior object that controls mock behavior. Use this object to define mock method and property behavior (stub). For more information on creating a mock, see Create Mock Object.

What is mock object in junit?

Mock objects is a unit testing technique in which a code chunk is replaced by dummy implementations that emulate real code. This helps one to write unit tests targeting the functionality provided by the class under test.


2 Answers

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);
like image 39
patrio2 Avatar answered Sep 23 '22 17:09

patrio2


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.

like image 147
Alexandre Dupriez Avatar answered Sep 24 '22 17:09

Alexandre Dupriez