【问题标题】:CompletableFuture usability and unit testCompletableFuture 可用性和单元测试
【发布时间】:2017-06-11 01:32:47
【问题描述】:

我正在学习 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();

关于我在这里缺少什么的任何提示?谢谢!

【问题讨论】:

    标签: java unit-testing testing junit mockito


    【解决方案1】:

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

    如果您想模拟您的Executor 的方法execute,您应该继续下一步,只需调用提供的Runnable 的方法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());
        }
    }
    

    【讨论】:

    • 这样做我会得到 NPE,因为我必须模拟我的 ExecutorService(我使用的是自定义的),但如果我使用模拟的,测试永远不会结束。当然,如果我使用默认的执行器服务,而不将我的发送到 CompletableFuture.supplyAsync(),它就像一个魅力。
    • 是的!谢谢!这就是我必须做的总是我想模拟 ExecutionService.execute() 方法?这正是我试图做的,但在某种程度上它是有效的。
    【解决方案2】:
      @Mock
      private AsyncExecuter asyncExecuter;
      @Mock
      private CompletableFuture<XyzSample> xyzSampleResponse;
      @Mock
      private CompletableFuture<Map<String, String>> abcSampleResponse;
    
     @Before
      public void setUp() throws Exception {
    
        abcSampleResponse = CompletableFuture.completedFuture(TestUtil.readJsonResource(
            "misc_mapper_response.json", new TypeReference<Map<String, String>>() {
            }));
    
        xyzSampleResponse = CompletableFuture.completedFuture(TestUtil.readJsonResource(
            "gp_facade_response.json", new TypeReference<XyzSample>() {
            }));
    
      }
    
       @Test
      public void testAbcMethod() {
    
        Mockito.doReturn(abcSampleResponse).when(asyncExecuter)
            .callPgEndpoint(TestConstants.TEST_CUSTOMER_ID);
    
        Mockito.doReturn(xyzSampleResponse).when(asyncExecuter)
            .getUserPreference(TestConstants.TEST_CUSTOMER_ID);
    
    
        final ActualResponse actualResponse = globalPositionService
            .getGlobalPosition(TestConstants.TEST_CUSTOMER_ID);
    
        assertNotNull(actualResponse);
    }
    
    =====Service
    public ActualResponse getGlobalPosition(final String customerId) {
    
        final CompletableFuture<Map<String, String>> abcSampleResponse = asyncExecuter
            .getProductTypeInfo();
        final CompletableFuture<XyzSample> xyzSampleResponse = asyncExecuter
            .getUserPreference(customerId);
    
        try {
          return new ResponseDecorator(pgResponse.get(), userPreferenceResponse.get(),
              productTypeInfo.get()).decorate();
        } catch (final Exception e) {
          log.error("Error Occurred while building the response", e);
        }
        return null;
      }
    
    @Component
    public class AsyncExecuter {
      public CompletableFuture<XyzSample> callPgEndpoint(final String customerId) {
        return CompletableFuture.completedFuture(xxx);
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-10-03
      • 2011-07-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多