JMockit:Mocked apis会在一段时间后恢复

时间:2015-05-10 10:35:06

标签: java bytecode jit jmockit java-bytecode-asm

我正在使用JMockit来模拟System.currentMillis() 很少有调用返回模拟时间,但过了一段时间后,它开始返回原始时间 当我在禁用JIT后运行相同的操作时,它运行得很好。

2 个答案:

答案 0 :(得分:3)

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

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并将模拟注入到依赖类中。

答案 1 :(得分:1)

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

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