测试带时间依赖的业务逻辑,核心是将“当前时间”抽象为可注入的TimeProvider接口,生产用系统时间、测试用固定模拟时间,结合Mockito @MockBean控制返回值,并针对超时、日切等场景设计边界用例,避免Thread.sleep。

测试带时间依赖的业务逻辑,核心是把“当前时间”从硬编码变成可注入、可控制的抽象,避免测试因系统时钟漂移、跨时区或执行时机不可控而失败。
用时间提供接口替代 System.currentTimeMillis() 或 LocalDateTime.now()
不要在业务方法里直接调用静态时间获取方法。定义一个接口,比如:
TimeProvider interface {
Instant now();
LocalDateTime nowLocal();
}
在服务类中通过构造函数或字段注入该接口实例。生产环境用真实实现(返回系统时间),测试时换为固定时间的模拟实现。
立即学习“Java免费学习笔记(深入)”;
✅ 好处:时间行为完全由测试控制;✅ 可复用;✅ 不污染业务主流程。
用 Mockito 模拟时间提供者
若 TimeProvider 已被 Spring 管理,可在测试中用 @MockBean 替换它:
@MockBean
private TimeProvider timeProvider;
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
然后在 @BeforeEach 中设定返回值:
when(timeProvider.now()).thenReturn(Instant.parse("2026-09-15T10:00:00Z"));
这样所有调用 timeProvider.now() 的地方都会返回你指定的时间点,测试结果稳定可预期。
测试典型时间场景
针对常见时间逻辑,设计明确的边界用例:
- 订单超时判断:设当前时间为创建时间 + 29 分钟 → 应未超时;设为 + 31 分钟 → 应标记为超时
- 日切逻辑:设时间为 2026-09-15T23:59:59 → 属于当日;设为 2026-09-16T00:00:00 → 应进入新一天
- 定时任务触发窗口:验证是否在 [start, end) 区间内才执行
每个用例都应断言具体行为,而不是只检查“有没有抛异常”。
避免使用 Thread.sleep() 等等待方式
单元测试里写 sleep 会拖慢执行、降低可靠性,且无法精确控制“那一刻”的状态。真正需要时间推进的场景(如延时队列模拟),应改用虚拟时钟(如 io.github.jan0703.virtuallight)或更推荐的方式——重构逻辑,让时间推进成为输入参数,例如:
processOrder(order, Instant.ofEpochMilli(1726423200000L));
这样测试时传入任意时刻,逻辑完全确定、无需等待。

















