How to override/control the way the JVM gets the system date?
How do you mock-up/trick the JVM to get a date other that the current system date? I have a set of tests in JUnit I don't want to change, but instead I want to change a se开发者_如何学Pythontting so that when the JVM retrieves the date it retrieves the date I want.
Have you done something similar before?
Thanks.
There are a couple of ways to do this:
if you can rewrite code - as mentioned in some other answers, basically you need a configurable NowProvider, DateFactory, or other strategy (I like injectable NowProvider.now() as a dropin replacement for System.currentTimeMillis myself)
Powermock, and some other toolkits let you use ClassLoader tricks to override static methods, even system methods
You could have a DateFactory
interface with two implementations, one which always returns new Date()
(this would be used in production), and a mock object which returns Date
objects which are more appropriate for your unit tests.
(This is assuming you can change your code to make it more amenable to unit testing.)
I would rather try to hide that functionality behind a mockable interface, so that the "real" implementation gets the system date, while your mock implementation returns whatever date you have configured.
Messing with the system date in unit tests doesn't sound a very nice thing to me - I would try to avoid it if I can. Unit tests should be context-independent as much as possible - that saves a lot of trouble in the long run.
However, if you can't modify the code you are testing, you may have no other way. The question then is, are you interested only in the date, or the full timestamp? The latter case sounds pretty hopeless to me to achieve (in any other way than the above mock interface), as I guess simply resetting the date before running your tests is not enough - you need a fixed instant of time to be returned throughout your unit tests. Maybe your tests run fast enough to finish before the clock ticks, maybe not - maybe they pass right now, then start to fail randomly sometime later after adding the next test :-(
If you only need the date though, you might try changing the date in the @BeforeClass
method, then resetting it in @AfterClass
, although it is gross.
You could write a TestRule for it. Something like this might work:
import org.joda.time.DateTime;
import org.joda.time.DateTimeUtils;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
public class FixedDateTimeRule implements TestRule {
private final DateTime fixedDate;
public FixedDateTimeRule(DateTime fixedDate) {
this.fixedDate = fixedDate;
}
/*
* (non-Javadoc)
*
* @see org.junit.rules.TestRule#apply(org.junit.runners.model.Statement, org.junit.runner.Description)
*/
@Override
public Statement apply(final Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
DateTimeUtils.setCurrentMillisFixed(fixedDate.getMillis());
try {
base.evaluate();
} finally {
DateTimeUtils.setCurrentMillisSystem();
}
}
};
}
}
@Rule
public FixedDateTimeRule fixedDateTime = new FixedDateTimeRule(new DateTime(...))
@Test
public void someKindOfTestThatNeedsAFixedDateTime() { ... }
精彩评论