I was investigating Mockito’s behavior in one of our tests and I didn’t understand its behavior. The following code snippets shows the same behavior :
@Test public void test(){ var mymock = Mockito.mock(Object.class); var l = List.of(1,2,3); l.parallelStream().forEach(val -> { int finalI = val; doAnswer(invocationOnMock -> { log.info("{}",finalI); return null; }).when(mymock).toString(); mymock.toString(); }); }
The output I was expecting is prints of 1,2,3 in some order (not nessaseraly sorted) :
The output I received :
2 2 2
Why I got this output ?
Advertisement
Answer
This is a classic race condition: your parallel stream executes the forEach
lambda three times in parallel. In this case, all three threads managed to execute the doAnswer()
call, before any of them got to the mymock.toString()
line. It also just so happens that the execution where val=2
happened to run last. Happily, Mockito is reasonably thread-safe, so you don’t just get exceptions thrown.
One way your scenario might occur is if the threads happened to run like this:
- Main thread: calls
l.parallelStream().forEach()
, which spawns 3 threads withval
equal to 1, 2 and 3 - val=1 thread: executes
finalI=val
, giving it a value of 1 in this thread - val=2 thread: executes
finalI=val
, giving it a value of 2 in this thread - val=3 thread: executes
finalI=val
, giving it a value of 3 in this thread - val=3 thread: calls
doAnswer(...)
which makes future calls tomymock.toString()
print out3
- val=1 thread: calls
doAnswer(...)
which makes future calls tomymock.toString()
print out1
- val=2 thread: calls
doAnswer(...)
which makes future calls tomymock.toString()
print out2
- val=1 thread: calls
mymock.toString()
, which prints out2
- val=2 thread: calls
mymock.toString()
, which prints out2
- val=3 thread: calls
mymock.toString()
, which prints out2
Note that this isn’t necessarily happening in order, or even in this order:
- some parts of this may literally be happening at the same time, in different cores of your processor.
- while any steps within a single thread would always run in order, they might run before, after, or interleaved with any steps from any other thread.
Because the threads are running in parallel, and you have not put anything in place to manage this, there is no guarantee whatsoever about the order in which these calls occur: your output could just as easily have been 1 1 1
, 1 2 3
, 3 2 1
or 3 2 2
or 1 1 3
etc.