Garmaine Staff asked 2 years ago

I have some code that performs some work in sequence, performs some operations in parallel, and then performs some more work in sequence once the parallel work is complete. I do not care in which order the parallel work happens, just that it all happens in the middle. Using Mockito, how can I verify that the parallel work happens in the middle of the sequential work, but without caring about the order of the middle parallel calls?

I know that I can use InOrder to verify a strict ordering amongst all the calls, or to exclude some calls from the ordering checks altogether, but this does not quite handle my use case. Additionally, since we're dealing with concurrent code, it doesn't handle the fact that ideally we'd want to verify that the final sequential methods should only get called after the parallel methods complete, not just after they're called.

public interface MockedClass {
    void initialMethodOne();
    void initialMethodTwo();
    void parallelMethodOne();
    void parallelMethodTwo();
    void finalMethodOne();
    void finalMethodTwo();
}
import java.util.concurrent.CompletableFuture;

public class InOrderExample {
    private MockedClass mockedClass;

    public InOrderExample(MockedClass mockedClass) {
        this.mockedClass = mockedClass;
    }

    public void doOrderedAndParallelWork() {
        mockedClass.initialMethodOne();
        mockedClass.initialMethodTwo();

        CompletableFuture.allOf(
                CompletableFuture.runAsync(mockedClass::parallelMethodOne),
                CompletableFuture.runAsync(mockedClass::parallelMethodTwo))
        .join();

        mockedClass.finalMethodOne();
        mockedClass.finalMethodTwo();
    }
}
import org.junit.Test;
import org.mockito.InOrder;

import static org.mockito.AdditionalAnswers.answersWithDelay;
import static org.mockito.Mockito.*;

public class InOrderExampleTest {
    @Test
    public void doOrderedAndParallelWork() {
        MockedClass mockedClass = mock(MockedClass.class);

        doAnswer(answersWithDelay(200, invocation -> null)).when(mockedClass).parallelMethodOne();
        doAnswer(answersWithDelay(100, invocation -> null)).when(mockedClass).parallelMethodTwo();

        new InOrderExample(mockedClass).doOrderedAndParallelWork();

        InOrder inOrder = inOrder(mockedClass);

        // These two must happen first, in order
        inOrder.verify(mockedClass).initialMethodOne();
        inOrder.verify(mockedClass).initialMethodTwo();

        // Not quite what I want; these two must happen after the first two and before the last two, but I don't want
        // the test to fail if these two are called in opposite order
        inOrder.verify(mockedClass).parallelMethodOne();
        inOrder.verify(mockedClass).parallelMethodTwo();

        // These two must happen last, in order
        inOrder.verify(mockedClass).finalMethodOne();
        inOrder.verify(mockedClass).finalMethodTwo();
    }
}