应使用 Clock 替换硬编码系统时间,通过依赖注入实现时间可控;统一用 LocalDate、Instant、ZonedDateTime 替代旧式 API;针对边界场景设计测试用例;重构静态工具类为可注入 Clock 的实例类。

直接测试依赖系统时间的逻辑,比如“获取当前日期”“计算过期时间”“判断是否在有效期内”,容易让测试结果飘忽不定——今天跑通过,明天就失败。问题不在代码本身,而在测试方式。关键不是“测时间”,而是“控时间”。
用 Clock 替换硬编码的系统时间
Java 8 的 java.time.Clock 是专为可测试性设计的时间抽象。不要写 LocalDateTime.now(),改用 LocalDateTime.now(clock),把 clock 作为参数或构造器注入。
- 生产环境传入
Clock.systemDefaultZone() - 单元测试时传入固定时间的 clock,例如
Clock.fixed(Instant.parse("2026-10-12T10:00:00Z"), ZoneId.of("UTC")) - 这样每次运行测试,时间点都完全一致,断言稳定可靠
避免 Date/Calendar + SimpleDateFormat 组合
旧式 API 隐含时区、毫秒精度、DST 跳变等陷阱,哪怕只比对日期(不关心时间),也极易因格式化截断或解析偏差导致 7 天变 6 或 8 天。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 统一使用 LocalDate 处理“仅日期”场景(如生日、合同起止日)
- 用 Instant 和 ZonedDateTime 处理带时区的瞬时事件(如订单创建时间、定时任务触发点)
- 禁止在测试中出现
new Date()、Calendar.getInstance()、SimpleDateFormat.parse()
针对时间逻辑设计边界测试用例
时间相关逻辑的脆弱点往往在边界:跨月、跨年、闰年、夏令时切换前后、零点、23:59:59 等。
立即学习“Java免费学习笔记(深入)”;
- 测试“有效期 30 天”:选一个固定起点(如 2026-01-31),验证到期日是 2026-02-28(非 03-02)
- 测试“本周一”:用
localDate.with(DayOfWeek.MONDAY),而非加减天数推算 - 测试“是否已过期”:分别用到期日当天、到期日+1纳秒、到期日-1纳秒构造 Instant 进行断言
静态工具类必须重构才能可靠测试
像 DateUtils.getNow() 这类静态方法,无法注入 Clock,Mockito 也不能直接 mock(除非用 PowerMock,但代价高、不推荐)。
- 将工具类改为实例类,构造器接收 Clock 参数
- 或提取出时间生成接口(如
TimeProvider),让工具类依赖它 - 测试时传入 Stub 实现,返回预设时间值

















