应优先使用 awaitility 声明式等待 future 完成再断言,语义清晰、线程安全、自带超时;若不引入依赖,则必须用 get(long, timeunit) 并设超时,避免无限阻塞。

直接用 get() 等待并断言容易卡死或超时,关键是要等结果就绪再校验,而不是盲目阻塞。核心是“先确认完成、再取值断言”,同时避免测试线程被挂起。
用 Awaitility 声明式等待完成
这是最推荐的方式——语义清晰、自带超时、线程安全,且不依赖手动 sleep 或轮询。
- 添加 Maven 依赖:
- 在测试中等待 future 完成后再断言:
CompletableFuture
Awaitility.await()
.atMost(3, TimeUnit.SECONDS)
.until(future::isDone);
Assertions.assertEquals("expected", future.get());
用 get() + 显式超时兜底
如果不想引入新依赖,可用原生 get(long, TimeUnit),但必须设超时,否则测试可能永远卡住。
- 示例写法:
CompletableFuture
try {
boolean result = future.get(5, TimeUnit.SECONDS);
Assertions.assertTrue(result);
fail("异步任务未在 5 秒内完成");
throw new RuntimeException(e);
}
Java开发手册规约集合,基于阿里巴巴Java开发手册(嵩山版)。 涵盖7大维度:编程规约、异常日志、单元测试、安全规约、MySQL数据库、工程结构、设计规约。 当用户需要:(1) 编写或审查Java代码 (2) 检查命名/代码规范 (3) 处理异常和日志 (4) 编写单元测试 (5) 安全编码 (6) 数据库设...
针对已知结果的场景,用 completedFuture 简化测试
当想绕过异步执行、专注验证后续逻辑(比如 thenApply 的转换逻辑)时,直接构造已完成的 CompletableFuture。
- 例如模拟成功响应:
CompletableFuture
String name = stub.thenApply(User::getName).join();
Assertions.assertEquals("test", name);
- 模拟异常响应:
CompletableFuture
failed.completeExceptionally(new RuntimeException("mock error"));
String fallback = failed.exceptionally(t -> new User("fallback")).join().getName();
Assertions.assertEquals("fallback", fallback);
避免常见陷阱
别只调用方法却不等结果:仅写 service.asyncCall() 而不检查返回值,SonarQube 会报 “Missing assertion”。
别用 join() 在测试里无保护调用:它不响应中断,也不支持超时,出问题时难以调试。
别在测试中用 ForkJoinPool.commonPool():可能与其他测试争抢线程,建议显式传入单线程或固定大小的测试专用 Executor。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










