在Mockito中,如何在顺序部分内部包含并行部分时进行顺序验证?

huangapple go评论81阅读模式
英文:

In Mockito, how can I verify order with a parallel section inside a sequential section?

问题

我有一些代码按顺序执行一些工作在并行中执行一些操作然后在并行工作完成后再按顺序执行一些工作我不关心并行工作发生的顺序只关心它们都在中间发生使用Mockito如何验证并行工作在顺序工作的中间发生但不关心中间并行调用的顺序

我知道可以使用[InOrder](https://javadoc.io/static/org.mockito/mockito-core/3.3.3/org/mockito/InOrder.html)来验证所有调用之间的严格顺序,或者从顺序检查中完全排除一些调用,但这并不完全适用于我的用例。此外,由于我们处理并发代码,它无法处理这样一个事实,即理想情况下,我们希望验证最终的顺序方法只能在并行方法完成后才被调用,而不仅仅是在它们被调用后。

```java
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);

        // 这两个方法必须首先按顺序执行
        inOrder.verify(mockedClass).initialMethodOne();
        inOrder.verify(mockedClass).initialMethodTwo();

        // 不完全符合我的要求;这两个方法必须在前两个方法之后且在最后两个方法之前执行,但我不希望测试在这两个方法的调用顺序相反时失败
        inOrder.verify(mockedClass).parallelMethodOne();
        inOrder.verify(mockedClass).parallelMethodTwo();

        // 这两个方法必须最后按顺序执行
        inOrder.verify(mockedClass).finalMethodOne();
        inOrder.verify(mockedClass).finalMethodTwo();
    }
}

<details>
<summary>英文:</summary>

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](https://javadoc.io/static/org.mockito/mockito-core/3.3.3/org/mockito/InOrder.html) 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&#39;re dealing with concurrent code, it doesn&#39;t handle the fact that ideally we&#39;d want to verify that the final sequential methods should only get called after the parallel methods complete, not just after they&#39;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 -&gt; null)).when(mockedClass).parallelMethodOne();
    doAnswer(answersWithDelay(100, invocation -&gt; 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&#39;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();
}

}



</details>


# 答案1
**得分**: 0

虽然我在[Mockito文档](https://www.javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#6)或其他地方没有明确看到,但实验表明可以同时使用多个`InOrder`对象来验证相同的调用。然后可以独立验证每条路径(如果有多个并行调用可能会在循环中进行)。

```java
@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();

    // 验证严格顺序的部分
    InOrder inOrder = inOrder(mockedClass);
    inOrder.verify(mockedClass).initialMethodOne();
    inOrder.verify(mockedClass).initialMethodTwo();
    inOrder.verify(mockedClass).finalMethodOne();
    inOrder.verify(mockedClass).finalMethodTwo();

    // 验证第一个调用相对于顺序部分的正确顺序
    InOrder inOrder1 = inOrder(mockedClass);
    inOrder1.verify(mockedClass).initialMethodTwo();
    inOrder1.verify(mockedClass).parallelMethodOne();
    inOrder1.verify(mockedClass).finalMethodOne();

    // 验证第二个调用相对于顺序部分的正确顺序
    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();
}
英文:

Although I don't see anywhere that it's explicitly called out in the Mockito documentation or elsewhere, experimentation has shown that it is possible to use multiple InOrder objects at the same time to verify the same calls. Once can then then verify each path independently (possibly in a loop if there are more than a couple parallel calls).

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

    doAnswer(answersWithDelay(200, invocation -&gt; null)).when(mockedClass).parallelMethodOne();
    doAnswer(answersWithDelay(100, invocation -&gt; 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();
}

One thing this does not solve, however, is the requirement that the final methods only get called after the parallel methods complete. Mockito does not appear to have native support for this sort of verification, but we can do the following as a workaround:

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

    doAnswer(answersWithDelay(200, invocation -&gt; {methodOneComplete.complete(); return null;}))
            .when(mockedClass).parallelMethodOne();
    doAnswer(answersWithDelay(100, invocation -&gt; {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();
}

huangapple
  • 本文由 发表于 2020年4月11日 07:08:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/61149975.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定