Awaitility 等待 payment state 时 10 秒后报错
支付老李
结论
问题不在 Awaitility,而是等待期间始终在检查同一个 rawRes。
Response 是请求完成时生成的响应快照。即使后台随后将 payment state 更新为 Refunded,原来的 rawRes`` 也不会随之变化。因此,Awaitility 在 10 秒内反复读取的都是旧状态,最后会抛出 ConditionTimeoutException`。
每次轮询时都应重新调用查询接口,再检查最新响应。
推荐写法
假设 getOrder(orderId) 会重新请求订单接口:
public static Response verifyLineItemIsRefunded(String orderId) {
AtomicReference<Response> latestResponse = new AtomicReference<>();
await()
.atMost(10, SECONDS)
.pollInterval(500, MILLISECONDS)
.untilAsserted(() -> {
Response response = getOrder(orderId);
latestResponse.set(response);
String paymentState = response.jsonPath()
.getString("returnInfo[0].items[0].paymentState");
assertThat(paymentState, equalToIgnoringCase("Refunded"));
});
logInfo("Line item is refunded");
return latestResponse.get();
}
需要的静态导入如下:
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.awaitility.Awaitility.await;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalToIgnoringCase;
import java.util.concurrent.atomic.AtomicReference;
getOrder(orderId) 只是示例名称,需要替换为项目中实际用于查询最新订单状态的方法。
如果不需要返回最新 Response
如果这个方法只负责验证状态,可以简化为:
public static void verifyLineItemIsRefunded(String orderId) {
await()
.atMost(10, SECONDS)
.pollInterval(500, MILLISECONDS)
.untilAsserted(() -> {
String paymentState = getOrder(orderId)
.jsonPath()
.getString("returnInfo[0].items[0].paymentState");
assertThat(paymentState, equalToIgnoringCase("Refunded"));
});
logInfo("Line item is refunded");
}
也可以直接等待布尔条件:
public static void verifyLineItemIsRefunded(String orderId) {
await()
.atMost(10, SECONDS)
.pollInterval(500, MILLISECONDS)
.until(() -> {
String paymentState = getOrder(orderId)
.jsonPath()
.getString("returnInfo[0].items[0].paymentState");
return "Refunded".equalsIgnoreCase(paymentState);
});
}
将常量放在前面调用 equalsIgnoreCase,可以避免 paymentState 为 null 时触发 NullPointerException。
为什么原代码不会成功
原代码的核心逻辑相当于:
await().untilAsserted(() -> check(rawRes));
Awaitility 会反复调用 check(rawRes),但不会重新执行生成 rawRes 的 HTTP 请求。实际流程是:
请求一次 -> 得到旧 Response -> 重复检查同一个 Response -> 10 秒后超时
应该改成:
重新请求 -> 检查最新 Response -> 未满足则稍后再次请求
注意事项
ConditionTimeoutException表示条件在规定时间内一直未通过,不代表 Awaitility 本身失效。10秒是否足够,取决于退款处理、消息队列、第三方支付和数据同步的实际耗时,不能只根据单次验证所需的时间判断。- 设置合理的
pollInterval,避免短时间内频繁调用接口。 - 如果单次 HTTP 请求可能长时间阻塞,还需要为 HTTP 客户端设置连接超时和读取超时。
- 确认 JSON 路径
returnInfo[0].items[0].paymentState指向目标 line item。订单中有多个 item 时,固定读取[0]可能会检查到错误的对象。 - 如果方法需要返回响应,返回值应是最后一次查询得到的
Response,否则调用方拿到的仍可能是最初的旧响应。
备注:内容仅供参考。
