本文介绍如何对返回 CompletableFuture<Boolean> 的异步服务方法编写合规、可验证的单元测试,重点解决 SonarQube 提示“缺少断言”的问题,并推荐使用 Awaitility 简化异步断言逻辑。
本文介绍如何对返回 `completablefuture
在 Spring Boot 项目中,使用 @Async 返回 CompletableFuture<Boolean> 是常见模式,但其异步特性给单元测试带来挑战:若仅调用方法而不等待结果并校验,测试将无法覆盖业务逻辑正确性,SonarQube 等质量门禁也会报出 “Add at least one assertion” 警告。
核心原则是遵循经典测试三段式:Given(准备输入)→ When(触发异步调用)→ Then(等待完成并断言结果)。由于 CompletableFuture 不会自动阻塞主线程,直接调用 .get() 可能导致无限等待(若未完成),因此需配合超时机制安全获取结果。
推荐使用 Awaitility —— 专为异步测试设计的轻量库,支持声明式等待条件,语义清晰且线程安全。
✅ 步骤一:添加依赖(Maven)
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<version>4.2.0</version>
<scope>test</scope>
</dependency>✅ 步骤二:编写健壮测试(含超时保护)
@Test
@DisplayName("Async process (1)")
void insertarSolicitudTest() throws BusinessException {
// Given: 准备请求对象与 Mock 行为
DocumentRequest request = documentRequest();
// 假设已配置好 @Mock 注入的 repository 行为(如 when(repo.save(...)).thenReturn(...))
// When: 触发异步方法
CompletableFuture<Boolean> future = asynEvaluaEstadoSiniestro.getEvaluacionEstadoSiniestro(request);
// Then: 使用 Awaitility 等待完成(最多 5 秒),再断言结果
Awaitility.await()
.atMost(5, TimeUnit.SECONDS)
.until(future::isDone);
try {
Boolean result = future.get(); // 非阻塞前提下安全获取(因 isDone 已为 true)
Assertions.assertTrue(result, "期望异步评估返回 true");
} catch (ExecutionException | InterruptedException e) {
throw new RuntimeException("异步执行异常", e);
}
}? 注意事项:
- 切勿仅调用 future.isDone() 而不 get() 或 join() —— 这不是有效断言,SonarQube 会忽略;
- future.get() 必须配合超时(如 get(5, SECONDS))或前置 isDone 检查,否则测试可能挂起;
- Awaitility 的 until(future::isDone) 是推荐方式,比手动 Thread.sleep() 更可靠、更易读;
- 若需验证 false 结果,使用 Assertions.assertFalse(result) 即可,逻辑保持一致。
✅ 替代方案(无第三方依赖)
若暂不能引入 Awaitility,可改用 CompletableFuture.join() + 显式超时:
Boolean result = future.orTimeout(5, TimeUnit.SECONDS).join(); Assertions.assertTrue(result);
但需注意:orTimeout 是 Java 9+ 特性,且抛出 CompletionException,需捕获处理。
综上,为 CompletableFuture<Boolean> 添加断言的关键在于 显式等待完成 + 安全获取结果 + 校验布尔值。采用 Awaitility 不仅满足静态检查要求,更能提升测试可维护性与可读性,是 Spring 异步测试的最佳实践之一。

















