【问题标题】:Need help in writing a Junit test (mock) for a source code using CompleteableFuture在使用 CompletableFuture 为源代码编写 Junit 测试(模拟)时需要帮助
【发布时间】:2019-09-03 23:10:02
【问题描述】:

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

在这里,我希望模拟具有可完成未来的源代码的单元测试。

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

针对单元测试的源代码:-

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 控制永远不会返回。
我尝试了一些朋友或同事建议的其他方法。结果和上面差不多。

【问题讨论】:

  • getSomeResultThruAsync 方法的意义何在?为什么在阻塞调用线程时启动异步操作?顺便说一句,您的return null; 语句不仅是糟糕的编码风格,而且无论如何它都是无效的,因为无法访问代码位置。此外,getSomeResultMethod的参数类型存在不一致;一旦你通过String,然后是Integer...
  • 问题中的代码不完整,因此无法重现您的问题。你能提供一个minimal reproducible example吗?另外,您是否尝试过调试您的测试?
  • @Holger:删除了原始问题中的空返回。在实际方法中输入的是一个字典,其中包含特定于内部团队的一些详细信息。并且输出是根据getSomeResultMethod的结果处理后的信息。
  • 这并不能解释为什么你在等待结果时使用CompletableFuture。可以直接调用getSomeResultMethod
  • @Holger:使用最新更改更新了代码。删除 get 并替换为 complete。在实际代码中,我们看到了 3 秒的改进。现在问题在于对源代码进行单元测试。现在有更多指针吗?

标签: unit-testing java-8 mockito completable-future


【解决方案1】:

假设,您想通过单元测试覆盖以下课程:

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. 1234563这就是为什么我用另一个例子来演示单元测试的方法CompletableFuture

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多