Skip to content
Advertisement

Mockito when() in parallel stream unexpected result

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:

  1. Main thread: calls l.parallelStream().forEach(), which spawns 3 threads with val equal to 1, 2 and 3
  2. val=1 thread: executes finalI=val, giving it a value of 1 in this thread
  3. val=2 thread: executes finalI=val, giving it a value of 2 in this thread
  4. val=3 thread: executes finalI=val, giving it a value of 3 in this thread
  5. val=3 thread: calls doAnswer(...) which makes future calls to mymock.toString() print out 3
  6. val=1 thread: calls doAnswer(...) which makes future calls to mymock.toString() print out 1
  7. val=2 thread: calls doAnswer(...) which makes future calls to mymock.toString() print out 2
  8. val=1 thread: calls mymock.toString(), which prints out 2
  9. val=2 thread: calls mymock.toString(), which prints out 2
  10. val=3 thread: calls mymock.toString(), which prints out 2

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.

User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement