如何使用 http 请求在 lambda 上对 whenCompleteAsync 进行单元测试?

How do I unit test whenCompleteAsync on a lambda with a http request?

我想为以下 class 创建一个单元测试:

@Service
public class XService{
    
    public String getSomething(String inputField) {
        final SomeEntity someEntity1 = new SomeEntity();
        final AtomicReference<Throwable> throwable = new AtomicReference<>();

        BiConsumer<Response, Throwable> consumer = (response, error) -> {
        if (error != null) {
            throwable.set(error);
        } else {
            SomeEntity someEntity2 = response.readEntity(SomeEntity.class);
            someEntity1.setSomeField(someEntity2.getSomeField());
            //does some stuff with the response
        }
        };
        
        WebTarget target = client.target("api_url"+inputField);
        target.queryParam("param", param)
            .request(MediaType.APPLICATION_JSON)
            .acceptLanguage(Locale.ENGLISH)
            .header("Authorization", token)
            .rx()
            .get()
            .whenCompleteAsync(consumer);

        return someEntity1.getSomeField();
    }
}

.whenCompleteAsync(consumer) 之前,我一直在嘲笑一切,使用的是这样的东西:

when(mockWebTarget.queryParam(any(),any())).thenReturn(mockWebTarget);
CompletionStageRxInvoker completionStageRxInvoker = mock(CompletionStageRxInvoker.class);
when(mockBuilder.rx()).thenReturn(completionStageRxInvoker);
CompletionStage<Response> mockResp = mock(CompletionStage.class);
when(completionStageRxInvoker.get()).thenReturn(mockResp);

我目前无法更改 class 的设计,只能对其进行测试。

如何模拟消费者对象以在 lambda 中生成代码 运行? 这甚至可能吗?

getSomething 方法存在竞争条件。无法可靠地测试它,因为它具有不确定的行为。

问题是 consumer 是在请求​​完成后异步调用的。 getSomething 中没有任何内容可以确保在 return someEntity1.getSomeField() 发生之前发生。这意味着它可能 return 从读取实体复制的字段,或者它可能 return 该字段的默认值。最有可能的是,它会在调用 consumer 之前 return (因为请求相对较慢)。请求完成后,它会设置 someEntity1 中的字段,但此时 getSomething 已经 return 向调用者提供了不正确的值,并且 someEntity1 引用的对象=] 不会再读了。

正确的处理方法是使 getSomething 也 return 成为 CompletionStage:

public CompletionStage<String> getSomething(String inputField) {
    WebTarget target = client.target("api_url"+inputField);
    return target.queryParam("param", param)
        .request(MediaType.APPLICATION_JSON)
        .acceptLanguage(Locale.ENGLISH)
        .header("Authorization", token)
        .rx()
        .get()
        .thenApply(response -> response.readEntity(SomeEntity.class).getSomeField());
}

然后,要对此进行单元测试,您可以像您一样为 WebTargetInvocation.BuilderCompletionStageRxInvokerResponse 创建模拟。与其模拟 CompletionStage,不如模拟 completionStageRxInvoker.get() 方法 return CompletableFuture.completedFuture(mockResponse) 更简单。请注意,CompletableFutureCompletionStage 的具体实现,它是 JavaSE 的一部分。

更好的是,为了减少模拟的扩散,您可以重构它以将请求逻辑与响应处理逻辑分开。像这样:

public CompletionStage<String> getSomething(String inputField) {
    return apiClient
            .get(inputField, param, token)
            .thenApply(SomeEntity::getSomeField);
}

其中 apiClient 是您可以模拟的自定义 class 或接口的注入实例,方法声明如下:

public CompletionStage<SomeEntity> get(String inputField, Object param, String token) {
    WebTarget target = client.target("api_url"+inputField);
    return target.queryParam("param", param)
            .request(MediaType.APPLICATION_JSON)
            .acceptLanguage(Locale.ENGLISH)
            .header("Authorization", token)
            .rx()
            .get()
            .thenApply(response -> response.readEntity(SomeEntity.class));
}