java - 如何在带有 http 请求的 lambda 上对 whenCompleteAsync 进行单元测试?
问题描述
我想为以下类创建一个单元测试:
@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);
我目前无法更改课程的设计,只能对其进行测试。
如何模拟消费者对象以使代码在 lambda 内运行?这甚至可能吗?
解决方案
该getSomething
方法具有竞争条件。不可能可靠地测试它,因为它具有不确定的行为。
问题是consumer
在请求完成后异步调用。没有任何东西getSomething
可以确保在发生之前return someEntity1.getSomeField()
发生。这意味着它可能会返回从读取实体复制的字段,或者可能会返回该字段的默认值。最有可能的是,它会在consumer
被调用之前返回(因为请求相对较慢)。一旦请求完成,它会设置 中的字段someEntity1
,但是此时,getSomething
已经向调用者返回了错误的值,并且someEntity1
不会再次读取 所引用的对象。
处理此问题的正确方法是getSomething
同时返回 a 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());
}
然后,要对此进行单元测试,您可以为WebTarget
、Invocation.Builder
、CompletionStageRxInvoker
和Response
像您所拥有的那样创建模拟。与模拟相比,返回模拟的方法CompletionStage
会更简单。请注意,它的具体实现是 JavaSE 的一部分。completionStageRxInvoker.get()
CompletableFuture.completedFuture(mockResponse)
CompletableFuture
CompletionStage
更好的是,为了减少模拟的扩散,您可以重构它以将请求逻辑与响应处理逻辑分开。像这样的东西:
public CompletionStage<String> getSomething(String inputField) {
return apiClient
.get(inputField, param, token)
.thenApply(SomeEntity::getSomeField);
}
您可以模拟的自定义类或接口的注入实例在哪里apiClient
,其方法声明如下:
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));
}
推荐阅读
- sql - 仅显示每月至少有一个条目的用户
- c# - 如何在 C# 中向 powershellinstance 添加参数
- firebase - Flutter:访问 Firebase 实时数据库中的数据
- r - 针对已知批次效应校正 RNA-seq 数据集
- python - 如何从 URL 下载图像并跳过 Python 中不存在的图像?
- python-3.x - 将列表中的所有元素与输入的每个条目匹配
- linux - 安装 .deb 包错误(dpkg 错误)Debian/Ubuntu 机器
- java - Java中的while循环::在不满足条件时继续划分
- python - 使用python的平面列表层次结构字符串
- c# - Dotnet Apache Spark - 对象引用未设置为对象的实例