JMockit: Erupted apis return after somtime

I am using JMockit to simulate System.currentMillis ().
Several invitations return the mocked time, but after a while it starts to return the original time.
When I run the same after disabling JIT, it works fine.

+3


source to share


2 answers


This is because the JIT optimizer in the JVM does not check for overridden methods (overriding is done through a different subsystem in the JVM). So ultimately the JVM decides to optimize the code containing the call in System.currentTimeMillis()

by inserting the call to the native

Java method so that it runs the actual native method directly. At this point, the optimizer must check whether it is overridden currentTimeMillis()

or not and discard the nesting if it is overridden. Unfortunately, the JDK engineers were unable to explain this possibility.



If you really need to call the mocked one too many times System.currentTimeMillis()

, the only workaround really should be done with -Xint

(which is not too bad since it usually reduces the overall test run time).

+1


source


Obviously, you have an important current time dependency inside one or more of your components. In this case, you have to express this dependency using an interface:

public interface TimeService {
    long currentTimeMillis();
}

      

In your real code, you have an implementation using the method System

:

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

      

Please note, as of Java 8, you can shrink the code to express it more clearly (thanks @Holger):

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

      

Your classes that depend on this time service should look like this:



public final ClassThatDependsOnTimeService {
    private final TimeService timeService;

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

    // other features omitted
}

      

They can now be fed with

TimeService timeService = new SystemTimeService();
ClassThatDependsOnTimeService someObject = new ClassThatDependsOnTimeService(timeService);

      

or (Java 8):

ClassThatDependsOnTimeService someObject = new ClassThatDependsOnTimeService(TimeService.DEFAULT);

      

or with any dependency injection infrastructure, etc.

In your tests, you are not mocking the method System.currentTimeMillis

, but you are mocking the interface TimeService

and injecting the mock into the dependent classes.

+3


source







All Articles