在使用 CompletableFuture 为源代码编写 Junit 测试(模拟)时需要帮助

Need help in writing a Junit test (mock) for a source code using CompleteableFuture

Objective 是为具有基于 CompleteableFuture 的异步操作的代码编写带有模拟的单元测试用例。
参考了各种例子(包括Whosebug),没有找到任何资料。 书籍、各种在线资源以及堆栈溢出中的大多数示例都指向在测试代码中使用 CompleteableFuture。

在这里,我希望对具有 completableFuture 的源代码进行模拟单元测试。

请提供任何指示,如果有的话。

用于单元测试的源代码:-

public String getSomeResultThruAsync(Integer a) {

        CompletableFuture<String> resultFuture;
        resultFuture = CompletableFuture.supplyAsync(() -> {
            try {
                return someFactory.getSomeResultMethod(a);
            }
            catch(Exception e) {
                throw new CustomException("custom code and message");
            }

        }, executorService);    // custom executorService initialized separately; to use apart from common pool 
        return resultFuture.complete();
    }

}

带有 Mock (mockito) 的单元测试代码:-

@Test
public void testGetSomeResultThruAsync()
    {
        // someFactory is a mock object of Factory class
        Mockito.when(someFactory.getSomeResultMethod(any(String.class))
               .thenReturn("test result");

        ...... Some base class code initialization, after that getSomeResultThruAsync method is called

    }

在通常情况下,上面的代码在模拟时工作正常。 此处为 completableFuture control never returns.
我尝试了朋友或同事建议的其他一些方法。结果与上面几乎相同。

假设,你想通过以下单元测试来覆盖 class:

private static class SomeClass {

    private final SomeFactory someFactory;
    private final Executor executorService;

    public SomeClass(SomeFactory someFactory, Executor executorService) {
        this.someFactory = someFactory;
        this.executorService = executorService;
    }

    public CompletableFuture<String> getSomeResultAsync(Integer a) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                return someFactory.getSomeResultMethod(a);
            } catch (Exception e) {
                throw new CustomException("custom code and message");
            }
        }, executorService);
    }
}

您可以这样做:

public class SomeClassTest {

    private Executor executor = Runnable::run; // same-thread executor
    private SomeFactory someFactory = Mockito.mock(SomeFactory.class);
    private SomeClass someClass = new SomeClass(someFactory, executor);

    @Test
    public void someResultIsReturnedIfAsyncCallCompletesWithoutErrors() throws Exception {
        Mockito.when(someFactory.getSomeResultMethod(1)).thenReturn("test");

        CompletableFuture<String> resultAsync = someClass.getSomeResultAsync(1);
        String result = resultAsync.get();

        Assertions.assertThat(result).isEqualTo("test");
    }

    @Test
    public void customExceptionIsThrownIfAsyncCallFailesWithError() throws Exception {
        Mockito.when(someFactory.getSomeResultMethod(1)).thenThrow(new RuntimeException());

        Assertions.assertThatThrownBy(() -> someClass.getSomeResultAsync(1).get())
                .isInstanceOf(ExecutionException.class)
                .hasCause(new CustomException("custom code and message"));
    }
}

备注:

  1. 在上面的测试中使用了org.assertj.core.api.Assertions

  2. 问题中的代码示例没有多大意义,因为您基本上只是将 someFactory.getSomeResultMethod(a) 包装在 CompletableFuture 中,然后调用 complete (我假设,你实际上是指 get)。这就是为什么我用另一个例子来演示单元测试的方法 CompletableFuture.