Scala中如何简便Mock java.time类调用?求类似Jest的时间模拟方案
在Scala中Mock java.time时间API的方案
1. 依赖注入Clock(推荐的标准方案)
java.time包的LocalDateTime.now()等方法默认使用系统时钟,我们可以通过依赖注入Clock来实现时间Mock,这是最贴合Java时间API设计的方式:
// 业务代码中依赖Clock,而非直接调用无参的now() class TimeDependentService(clock: Clock = Clock.systemDefaultZone()) { def getCurrentTime(): LocalDateTime = LocalDateTime.now(clock) } // 测试时传入固定时钟 import java.time.{Clock, LocalDateTime, ZoneId} val fixedInstant = LocalDateTime.of(2024, 5, 20, 14, 30).atZone(ZoneId.systemDefault()).toInstant() val fixedClock = Clock.fixed(fixedInstant, ZoneId.systemDefault()) val service = new TimeDependentService(fixedClock) assert(service.getCurrentTime() == LocalDateTime.of(2024, 5, 20, 14, 30)) // 也可以创建可调整的时钟,模拟时间流逝 val adjustableClock = Clock.tick(fixedClock, java.time.Duration.ofHours(1))
2. 自定义时间提供者Trait
如果需要更灵活的时间控制(比如批量调整时间),可以封装一个时间提供者Trait,在测试时替换实现:
trait TimeProvider { def now(): LocalDateTime def advanceBy(duration: java.time.Duration): Unit // 可选:添加时间推进方法 } // 生产环境实现 class SystemTimeProvider extends TimeProvider { override def now(): LocalDateTime = LocalDateTime.now() override def advanceBy(duration: java.time.Duration): Unit = () // 生产环境无操作 } // 测试环境Mock实现 class MockTimeProvider(var currentTime: LocalDateTime) extends TimeProvider { override def now(): LocalDateTime = currentTime override def advanceBy(duration: java.time.Duration): Unit = { currentTime = currentTime.plus(duration) } } // 业务代码中注入TimeProvider class OrderService(timeProvider: TimeProvider) { def createOrder(): Order = Order(createdAt = timeProvider.now()) } // 测试示例 val fixedTime = LocalDateTime.of(2024, 5, 20, 10, 0) val mockProvider = new MockTimeProvider(fixedTime) val service = new OrderService(mockProvider) assert(service.createOrder().createdAt == fixedTime) mockProvider.advanceBy(java.time.Duration.ofDays(1)) assert(service.createOrder().createdAt == fixedTime.plusDays(1))
3. 使用Mockito Mock静态方法(不推荐,仅用于遗留代码)
如果无法修改现有代码(必须直接MockLocalDateTime.now()静态调用),可以使用Mockito-inline来Mock静态方法:
import org.mockito.Mockito._ import java.time.LocalDateTime // 启用静态Mock val mockStatic = mockStatic(classOf[LocalDateTime]) when(LocalDateTime.now()).thenReturn(LocalDateTime.of(2024, 1, 1, 12, 0)) // 执行需要测试的代码,此时调用LocalDateTime.now()会返回固定值 // 验证调用并清理Mock verifyStatic(classOf[LocalDateTime], times(1)) LocalDateTime.now() mockStatic.close()
注意:静态Mock容易导致测试污染,需要确保每个测试用例都正确清理,仅在无法重构代码时使用。
内容的提问来源于stack exchange,提问作者Miguel Salas
相关产品推荐
相关产品推荐

