java - CompletableFuture 可用性和单元测试

标签 java unit-testing testing junit mockito

我正在学习 java 8 CompletableFuture 并以这个结束。

首先,您如何看待这行代码?我需要并行向不同服务发送请求,然后等待所有服务响应并继续工作。

//service A
CompletableFuture<ServiceAResponse> serviceAFuture = CompletableFuture.supplyAsync(
    () -> this.ServiceA.retrieve(serviceARequest), serviceAExecutorService
);

//service B
CompletableFuture<ServiceBResponse> serviceBFuture = CompletableFuture.supplyAsync(
    () -> this.ServiceB.retrieve(serviceBRequest), serviceBExecutorService
);

CompletableFuture.allOf(serviceAFuture, serviceBFuture).join();
ServiceAResponse responseA = serviceAFuture.join();
ServiceBResponse responseB = serviceBFuture.join();

即使代码正在执行我想要的操作,我在测试该代码所在的类时也遇到了问题。我尝试使用 Mockito 并执行类似的操作:

doAnswer(invocation -> CompletableFuture.completedFuture(this.serviceAResponse))
    .when(this.serviceAExecutorService)
    .execute(any());

执行者服务和服务响应是模拟的,但测试永远不会结束,线程一直在等待这一行中的东西

CompletableFuture.allOf(serviceAFuture, serviceBFuture).join();

关于我在这里遗漏了什么的任何提示?谢谢!

最佳答案

如果我是你,我会简单地模拟服务 A 和 B 以及你的执行程序,然后通过注释 @InjectMocks 注入(inject)它们因为它们是你类(class)的领域。

如果你想模拟 Executor 的方法 execute,你应该像下一步一样简单地调用提供的方法 run 可运行:

doAnswer(
    (InvocationOnMock invocation) -> {
        ((Runnable) invocation.getArguments()[0]).run();
        return null;
    }
).when(serviceAExecutorService).execute(any(Runnable.class));

所以基本上你的测试应该是这样的:

@RunWith(MockitoJUnitRunner.class)
public class CompletableFutureServiceTest {

    // The mock of my service A
    @Mock
    private ServiceA ServiceA;
    // The mock of my service B
    @Mock
    private ServiceB ServiceB;
    // The mock of your executor for the service A
    @Mock
    private Executor serviceAExecutorService;
    // The mock of your executor for the service B
    @Mock
    private Executor serviceBExecutorService;
    // My class in which I want to inject the mocks
    @InjectMocks
    private CompletableFutureService service;

    @Test
    public void testSomeMethod() {
        // Mock the method execute to call the run method of the provided Runnable
        doAnswer(
            (InvocationOnMock invocation) -> {
                ((Runnable) invocation.getArguments()[0]).run();
                return null;
            }
        ).when(serviceAExecutorService).execute(any(Runnable.class));
        doAnswer(
            (InvocationOnMock invocation) -> {
                ((Runnable) invocation.getArguments()[0]).run();
                return null;
            }
        ).when(serviceBExecutorService).execute(any(Runnable.class));

        ServiceAResponse serviceAResponse = ... // The answer to return by service A
        // Make the mock of my service A return my answer
        when(ServiceA.retrieve(any(ServiceARequest.class))).thenReturn(
            serviceAResponse
        );
        ServiceBResponse serviceBResponse = ... // The answer to return by service B
        // Make the mock of my service B return my answer
        when(ServiceB.retrieve(any(ServiceBRequest.class))).thenReturn(
            serviceBResponse
        );

        // Execute my method
        ServiceResponse response = service.someMethod(
            new ServiceARequest(), new ServiceBRequest()
        );

        // Test the result assuming that both responses are wrapped into a POJO
        Assert.assertEquals(serviceAResponse, response.getServiceAResponse());
        Assert.assertEquals(serviceBResponse, response.getServiceBResponse());
    }
}

关于java - CompletableFuture 可用性和单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41858894/

相关文章:

html - 有测试网站吗?

java - 如何仅在 x :th call? 上返回模拟项目

java - 将对象数组转换为自定义类数组

java - 有没有办法在不创建流程实例的情况下获取Activiti流程定义中的任务?

java - 为什么我的 DataPoints 方法被多次调用?

java - 使用 Mockito 验证递归方法调用的最佳实践

java - 为什么仅在生产环境中针对 Autowire 问题进行 Spring 循环引用?

java - 如何让线程一个接一个地运行?

Python SQLAlchemy - 模拟模型属性的 "desc"方法

android - 无法使用 Proguard 在 Android 项目上设置 Wiremock(重复 Zip 条目错误)