JMockit:模拟的 api 会在一段时间后恢复

JMockit: Mocked apis are getting reverted after sometime

我正在使用 JMockit 模拟 System.currentMillis()。
很少有调用返回模拟时间,但一段时间后,它开始返回原始时间。
当我禁用 JIT 后 运行 相同时,它 运行 完全没问题。

您显然对一个或多个组件中的当前时间有重要的依赖性。在这种情况下,您应该使用接口表达这种依赖关系:

public interface TimeService {
    long currentTimeMillis();
}

在您的实际代码中,您有一个使用 System 方法的实现:

public final SystemTimeService implements TimeService {
    @Override
    public long currentTimeMillis() {
        return System.currentTimeMillis();
    }
}

注意,使用 Java 8 可以减少一些代码以更清楚地表达它(感谢@Holger):

public interface TimeService {
    static final DEFAULT = System::currentTimeMillis;
    long currentTimeMillis();
}

您依赖于此时间服务的 类 应该如下所示:

public final ClassThatDependsOnTimeService {
    private final TimeService timeService;

    public ClassThatDependsOnTimeService(TimeService timeService) {
        this.timeService = timeService;
    }

    // other features omitted
}

现在可以用

喂它们了
TimeService timeService = new SystemTimeService();
ClassThatDependsOnTimeService someObject = new ClassThatDependsOnTimeService(timeService);

或 (Java 8):

ClassThatDependsOnTimeService someObject = new ClassThatDependsOnTimeService(TimeService.DEFAULT);

或任何依赖注入框架或其他任何东西。

在你的测试中你不模拟方法 System.currentTimeMillis 但你模拟接口 TimeService 并将模拟注入到依赖 类.

发生这种情况是因为 JVM 中的 JIT 优化器不检查重新定义的方法(重新定义是通过 JVM 中的不同子系统完成的)。因此,最终 JVM 决定优化包含对 System.currentTimeMillis() 的调用的代码,内联对 native Java 方法的调用,以便它直接开始执行实际的本机方法。此时,优化器应该检查 currentTimeMillis() 当前是否被重新定义,并在被重新定义的情况下放弃内联。但是,不幸的是,JDK 工程师未能考虑到这种可能性。

如果你真的需要多次调用模拟的 System.currentTimeMillis(),唯一的解决方法确实是 运行 和 -Xint(这不是一个坏主意,因为它通常会减少测试的总执行时间 运行).