You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Groovy+Spock测试中如何Mock new Date()/System.currentTimeMillis()?

解决方案:在Spock中控制new Date()返回值

针对你遇到的问题,以下是几种可行的解决方案,按推荐程度排序:

1. 重构代码:抽象日期获取逻辑(长期最优方案)

避免直接mock原生类,把日期获取抽象为可替换的依赖,测试时直接注入模拟实现:

步骤1:新增日期提供接口

interface DateProvider {
    Date getCurrentDate()
}

class DefaultDateProvider implements DateProvider {
    @Override
    Date getCurrentDate() {
        return new Date()
    }
}

步骤2:修改被测服务依赖

给被测服务添加构造器或setter注入DateProvider:

class TargetService {
    private DateProvider dateProvider = new DefaultDateProvider()

    // 测试时用setter替换实现
    void setDateProvider(DateProvider dateProvider) {
        this.dateProvider = dateProvider
    }

    def businessMethod() {
        // 用dateProvider替代直接new Date()
        Date currentDate = dateProvider.getCurrentDate()
        // 原有业务逻辑
    }
}

步骤3:Spock测试中模拟日期

def "测试7天后日期的业务逻辑"() {
    given:
    Date fixedDate = new Date() + 7
    DateProvider mockProvider = Mock()
    mockProvider.getCurrentDate() >> fixedDate
    
    TargetService service = new TargetService()
    service.setDateProvider(mockProvider)

    when:
    def result = service.businessMethod()

    then:
    // 断言结果符合预期
    result == expectedValue
}

这种方式完全避开了mock原生类的问题,代码解耦性更强,测试也更稳定。

2. Groovy MetaClass 修改构造器(适用于Groovy编写的被测代码)

如果被测代码是Groovy写的,可以直接修改Date的无参构造器元类,同时用Spock的@ConfineMetaClassChanges注解隔离测试,避免影响其他用例:

@ConfineMetaClassChanges(Date)
def "测试Groovy类中的固定日期逻辑"() {
    given:
    Date fixedDate = new Date() + 7
    // 重写Date的无参构造器
    Date.metaClass.constructor = { -> fixedDate }

    when:
    def result = new TargetGroovyService().businessMethod()

    then:
    result符合预期
}

注意:此方法仅对Groovy代码生效,Java代码调用new Date()不会走Groovy的元类逻辑。

3. PowerMock + Spock 组合(适用于Java编写的被测代码)

如果被测代码是Java写的,且无法重构,可使用PowerMock来mockDate的构造器,需注意正确配置依赖和注解:

依赖配置(Gradle示例)

testImplementation 'org.powermock:powermock-module-junit4:2.0.9'
testImplementation 'org.powermock:powermock-api-mockito2:2.0.9'
testImplementation 'org.spockframework:spock-core:2.0-groovy-3.0'

测试代码

@RunWith(PowerMockRunner.class)
@PrepareForTest(Date.class)
class TargetJavaServiceSpec extends Specification {

    def "测试Java类中的new Date()返回固定日期"() {
        given:
        Date fixedDate = new Date() + 7
        // mock Date的无参构造器
        PowerMockito.whenNew(Date.class).withNoArguments().thenReturn(fixedDate)

        when:
        def result = new TargetJavaService().businessMethod()

        then:
        result符合预期
    }
}

常见失效原因排查

  • 未添加@PrepareForTest(Date.class):PowerMock需要提前准备要mock的类
  • 依赖版本不兼容:确保PowerMock、Mockito、Spock版本匹配
  • 被测代码中new Date()是在静态方法内调用:需在@PrepareForTest中包含静态方法所在的类

内容的提问来源于stack exchange,提问作者felipe mota effting

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 06:16:13