В Mockito, как я могу проверить порядок с параллельным разделом внутри последовательного раздела? - PullRequest
1 голос
/ 11 апреля 2020

У меня есть некоторый код, который выполняет некоторую работу последовательно, выполняет некоторые операции параллельно, а затем выполняет некоторую дополнительную работу последовательно, когда параллельная работа завершена. Мне все равно, в каком порядке происходит параллельная работа, просто все это происходит посередине. Используя Mockito, как я могу проверить, что параллельная работа происходит в середине последовательной работы, но не заботясь о порядке средних параллельных вызовов?

Я знаю, что могу использовать InOrder для проверки строгого порядка среди всех вызовов или полного исключения некоторых вызовов из проверок порядка, но это не совсем подходит для моего варианта использования. Кроме того, поскольку мы имеем дело с параллельным кодом, он не учитывает тот факт, что в идеале мы хотели бы проверить, что окончательные последовательные методы должны вызываться только после завершения параллельных методов, а не только после их вызова.

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();
    }
}

1 Ответ

0 голосов
/ 11 апреля 2020

Хотя я нигде не вижу, чтобы это явно вызывалось в документации Mockito или где-либо еще, эксперименты показали, что можно использовать несколько InOrder объектов одновременно для проверки того же звонки. Однажды можно затем проверить каждый путь независимо (возможно, в al oop, если существует более пары параллельных вызовов).

@Test
public void doOrderedAndParallelWork2() {
    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();

    // Verify the strictly sequential portion
    InOrder inOrder = inOrder(mockedClass);
    inOrder.verify(mockedClass).initialMethodOne();
    inOrder.verify(mockedClass).initialMethodTwo();
    inOrder.verify(mockedClass).finalMethodOne();
    inOrder.verify(mockedClass).finalMethodTwo();

    // Verify the first call is executed in the right order relative to the sequential portion
    InOrder inOrder1 = inOrder(mockedClass);
    inOrder1.verify(mockedClass).initialMethodTwo();
    inOrder1.verify(mockedClass).parallelMethodOne();
    inOrder1.verify(mockedClass).finalMethodOne();

    // Verify the second call is executed in the right order relative to the sequential portion
    InOrder inOrder2 = inOrder(mockedClass);
    inOrder2.verify(mockedClass).initialMethodTwo();
    inOrder2.verify(mockedClass).parallelMethodTwo();
    inOrder2.verify(mockedClass).finalMethodOne();
}

Однако, одна вещь, которая не решает, - это требование, чтобы окончательный вариант методы вызываются только после завершения параллельных методов. Mockito, похоже, не имеет встроенной поддержки для такого рода проверки, но мы можем сделать следующее в качестве обходного пути:

@Test
public void doOrderedAndParallelWork3() {
    MockedClass mockedClass = mock(MockedClass.class);
    CheckComplete methodOneComplete = mock(CheckComplete.class);
    CheckComplete methodTwoComplete = mock(CheckComplete.class);

    doAnswer(answersWithDelay(200, invocation -> {methodOneComplete.complete(); return null;}))
            .when(mockedClass).parallelMethodOne();
    doAnswer(answersWithDelay(100, invocation -> {methodTwoComplete.complete(); return null;}))
            .when(mockedClass).parallelMethodTwo();

    new InOrderExample(mockedClass).doOrderedAndParallelWork();

    InOrder inOrder = inOrder(mockedClass, methodOneComplete, methodTwoComplete);
    inOrder.verify(mockedClass).initialMethodOne();
    inOrder.verify(mockedClass).initialMethodTwo();
    inOrder.verify(mockedClass).finalMethodOne();
    inOrder.verify(mockedClass).finalMethodTwo();

    InOrder inOrder1 = inOrder(mockedClass, methodOneComplete);
    inOrder1.verify(mockedClass).initialMethodTwo();
    inOrder1.verify(methodOneComplete).complete();
    inOrder1.verify(mockedClass).finalMethodOne();

    InOrder inOrder2 = inOrder(mockedClass, methodTwoComplete);
    inOrder2.verify(mockedClass).initialMethodTwo();
    inOrder2.verify(methodTwoComplete).complete();
    inOrder2.verify(mockedClass).finalMethodOne();
}

private interface CheckComplete {
    void complete();
}
...