Override Java System.currentTimeMillis for testing time sensitive code
Asked Answered
E

12

172

Is there a way, either in code or with JVM arguments, to override the current time, as presented via System.currentTimeMillis, other than manually changing the system clock on the host machine?

A little background:

We have a system that runs a number of accounting jobs that revolve much of their logic around the current date (ie 1st of the month, 1st of the year, etc)

Unfortunately, a lot of the legacy code calls functions such as new Date() or Calendar.getInstance(), both of which eventually call down to System.currentTimeMillis.

For testing purposes, right now, we are stuck with manually updating the system clock to manipulate what time and date the code thinks that the test is being run.

So my question is:

Is there a way to override what is returned by System.currentTimeMillis? For example, to tell the JVM to automatically add or subtract some offset before returning from that method?

Electroencephalograph answered 4/1, 2010 at 19:39 Comment(5)
I don't know whether it's relevant anymore, but there is another method to achieve this with AspectJ, see my answer at: #18240359Guizot
@NándorElődFekete the solution in the link is interesting, however it requires recompiling the code. I wonder if the original poster has the ability to recompile, given the fact he's claiming to be dealing with legacy code.Heredity
@Heredity One of the nice properties of AspectJ is that is operates on bytecode directly, so it doesn't need the original source code to work.Guizot
@NándorElődFekete thank you so much for the hint. I was unaware of the aspectJ bytecode-level instrumentation (especially the JDK classes instrumentation). It took me a while but I was able to figure out I had to both a compile-time weaving of the rt.jar as well as a load-time weaving of the non-jdk classes in order to suit my needs (override System.currentTimeMillis() and System.nanoTime()).Heredity
@Heredity I have another answer for weaving through JRE classes, you can check it out as well.Guizot
I
146

I strongly recommend that instead of messing with the system clock, you bite the bullet and refactor that legacy code to use a replaceable clock. Ideally that should be done with dependency injection, but even if you used a replaceable singleton you would gain testability.

This could almost be automated with search and replace for the singleton version:

  • Replace Calendar.getInstance() with Clock.getInstance().getCalendarInstance().
  • Replace new Date() with Clock.getInstance().newDate()
  • Replace System.currentTimeMillis() with Clock.getInstance().currentTimeMillis()

(etc as required)

Once you've taken that first step, you can replace the singleton with DI a bit at a time.

Integration answered 4/1, 2010 at 19:43 Comment(24)
+1 messing with the system clock is going to cause all kinds of problems. You're also relying on nothing else (like ntpd) to sync the clock and reset it.Fetal
@Stephen: Joda Time doesn't expose a clock type as far as I can tell, unfortunately. Noda Time does :)Integration
Thanks for the recommendation. I was figuring a refactor was going to be our best bet.Electroencephalograph
Cluttering your code by abstracting or wrapping all potential APIs to increase testability is IMHO not a very good idea. Even if you can do the refactoring once with a simple search & replace, the code becomes much harder to read, understand and maintain. Several mock frameworks ought to be able to modify the behaviour of System.currentTimeMillis, if not, using AOP or self-made instrumentalization is the better choice.Departmentalism
@jarnbjo: Well, you're welcome to your opinion of course - but this is a pretty well-established technique IMO, and I've certainly used it to great effect. Not only does it improve testability - it also makes your dependency on the system time explicit, which can be useful when getting an overview of the code.Integration
@jarnbjo: I agree with you - there are libraries you may rely on and they will use plain System.millis anyway. But do you have reliable solution for mocking/wrapping System.currentTimeMillis? I used jmockit, worked well - for a few calls. Then Sun's HotSpot messed up and method returned original value even though the jmockit code was still executed - just the results were ignored somehow. Do you have real reliable solution somewhere? Turning off hotspot is no option at all.Rosalbarosalee
@virgo47: I don't have the code at hand right now, but I have implemented an instrumentation based replacement of System.currentTimeMillis to solve exactly the same problem. Since Java 6, you can use instrumentation to replace native methods as well and directly "patch" the java.lang.System class with your own implementation. In Java 5, you can use instrumentation to replace all invocations of System.currentTimeMillis with an invocation of your own implementation (which has to be defined with the same method signature in another class).Departmentalism
My answer got deleted by TheifMaster (don't know why), I think it's ultimate answer without using DateFactory of any kind. Use jmockit with -XX:-Inline to prevent date returning to the current one. More is in my post: virgo47.wordpress.com/2012/06/22/changing-system-time-in-javaRosalbarosalee
@virgo47: Sounds like a hack to me - I'd much rather make the dependency clear.Integration
@JonSkeet I admit it's a hack, but so far it's the only solution that answers the question without polluting the code everywhere. For testing purposes this is perfectly acceptable (as mocking generally is).Rosalbarosalee
@virgo47: I think we'll have to agree to disagree. I don't see "stating your dependencies explicitly" as polluting the code - I see it as a natural way of making the code clearer. Nor do I see hiding those dependencies via static calls unnecessarily as "perfectly acceptable".Integration
@JonSkeet Ok, we disagree (no problem with that). I came up with that solution for my own needs and it offers one big advantage - works with any code, not just the one you can control. Many libraries would use current time instead of your time, some Bean Validation annotation would not work as expected, the list goes on. For testing purposes this is perfectly acceptable to me. I'd not use it in production. Biggest shame is that this whole question is not answered by the platform (Java/API/JVM) already. I hope we can agree at least on that. :-)Rosalbarosalee
@virgo47: I find it a shame that Joda Time doesn't support decent testability either - defaulting to the current system time all over the place, and not having a clock interface...Integration
For code you can control, Jons suggestion is the best. Unfortunately you may have to use libraries you cannot control :(Madson
UPDATE The new java.time package built into Java 8 includes a java.time.Clock class "to allow alternate clocks to be plugged in as and when required".Bighorn
This answer implies that DI is all good. Personally, I am yet to see an application in the real world which makes good use of it. Instead, we see a profusion of pointless separate interfaces, stateless "objects", data-only "objects", and low cohesion classes. IMO, simplicity and object-oriented design (with true, statefull objects) is a better choice. Regarding static methods, sure they are not OO, but injecting a stateless dependency whose instance don't operate on any instance state is not really better; it's just a fancy way of disguising what is effectively "static" behavior anyway.Caleb
@Rogério: We've had this argument before, and I don't see any likelihood that either of us will change each other's mind. I see no benefit in having the same argument in the comment thread of every answer which talks about this...Integration
@JonSkeet, I agree with you approach and can follow that for the code we control But what do you suggest about the 3rd party dependency which might relies on System.currentTimeMillis()? Any suggestions in that situation?Immortality
@PrashantKalkar: At that point, it's very hard to test without more invasive tools.Integration
Yet another incorrect answer. The question was how to mock system time.Fahlband
@azis.mrazish: I was addressing the underlying reason for the question, rather than just the literal question. (I'm not sure what the context of the "Yet another" part of your comment is... if you're suggesting that I regularly post incorrect answers, that's pretty rude.)Integration
@JonSkeet Nothing personal, but I think that 'addressing the underlying reason' it is common fallacy in public forums, and 'yet' is result of me seeing such kind of answers as accepted in stackoverflow. I come here to check out how do people solve this valid particular case: test 3rd party and legacy code without replacing datetime management, and what I see ' replace Date with Clock..' and so on, as acepted answer. This is not even fun. In case if my such answer would be accepted, I'd rather delete it myself, for great justice. AspectJ weaving must be the accepted one here.Fahlband
@azis.mrazish: Well given that the person who asked the question is the one who gets to decide what's accepted, I think they're the best person to decide what was actually most helpful to them.Integration
@JonSkeet "Yet another" is not addressing just your answer, it is addresses the way usual discussion communities do mislead people every time they ask. I.e. the way like 'I want to do this, tell me how' followed by 'No. do another thing' does not provide correct learning feedback to asking person. Here you can argue that relying on system time is design flaw, but it is secondary to question, and there may thirdparty library tested etc. Even if one asks 'how to weld the wood', you shall not start with 'go hammer a nail', before checking that wood welding is actually possible.Fahlband
B
107

tl;dr

Is there a way, either in code or with JVM arguments, to override the current time, as presented via System.currentTimeMillis, other than manually changing the system clock on the host machine?

Yes.

Instant.now( 
    Clock.fixed( 
        Instant.parse( "2016-01-23T12:34:56Z"), ZoneOffset.UTC
    )
)

Clock In java.time

We have a new solution to the problem of a pluggable clock replacement to facilitate testing with faux date-time values. The java.time package in Java 8 includes an abstract class java.time.Clock, with an explicit purpose:

to allow alternate clocks to be plugged in as and when required

You could plug in your own implementation of Clock, though you likely can find one already made to meet your needs. For your convenience, java.time includes static methods to yield special implementations. These alternate implementations can be valuable during testing.

Altered cadence

The various tick… methods produce clocks that increment the current moment with a different cadence.

The default Clock reports a time updated as frequently as milliseconds in Java 8 and in Java 9 as fine as nanoseconds (depending on your hardware). You can ask for the true current moment to be reported with a different granularity.

False clocks

Some clocks can lie, producing a result different than that of the host OS’ hardware clock.

  • fixed - Reports a single unchanging (non-incrementing) moment as the current moment.
  • offset - Reports the current moment but shifted by the passed Duration argument.

For example, lock in the first moment of the earliest Christmas this year. in other words, when Santa and his reindeer make their first stop. The earliest time zone nowadays seems to be Pacific/Kiritimati at +14:00.

LocalDate ld = LocalDate.now( ZoneId.of( "America/Montreal" ) );
LocalDate xmasThisYear = MonthDay.of( Month.DECEMBER , 25 ).atYear( ld.getYear() );
ZoneId earliestXmasZone = ZoneId.of( "Pacific/Kiritimati" ) ;
ZonedDateTime zdtEarliestXmasThisYear = xmasThisYear.atStartOfDay( earliestXmasZone );
Instant instantEarliestXmasThisYear = zdtEarliestXmasThisYear.toInstant();
Clock clockEarliestXmasThisYear = Clock.fixed( instantEarliestXmasThisYear , earliestXmasZone );

Use that special fixed clock to always return the same moment. We get the first moment of Christmas day in Kiritimati, with UTC showing a wall-clock time of fourteen hours earlier, 10 AM on the prior date of the 24th of December.

Instant instant = Instant.now( clockEarliestXmasThisYear );
ZonedDateTime zdt = ZonedDateTime.now( clockEarliestXmasThisYear );

instant.toString(): 2016-12-24T10:00:00Z

zdt.toString(): 2016-12-25T00:00+14:00[Pacific/Kiritimati]

See live code in IdeOne.com.

True time, different time zone

You can control which time zone is assigned by the Clock implementation. This might be useful in some testing. But I do not recommend this in production code, where you should always specify explicitly the optional ZoneId or ZoneOffset arguments.

You can specify that UTC be the default zone.

ZonedDateTime zdtClockSystemUTC = ZonedDateTime.now ( Clock.systemUTC () );

You can specify any particular time zone. Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).

ZonedDateTime zdtClockSystem = ZonedDateTime.now ( Clock.system ( ZoneId.of ( "America/Montreal" ) ) );

You can specify the JVM’s current default time zone should be the default for a particular Clock object.

ZonedDateTime zdtClockSystemDefaultZone = ZonedDateTime.now ( Clock.systemDefaultZone () );

Run this code to compare. Note that they all report the same moment, the same point on the timeline. They differ only in wall-clock time; in other words, three ways to say the same thing, three ways to display the same moment.

System.out.println ( "zdtClockSystemUTC.toString(): " + zdtClockSystemUTC );
System.out.println ( "zdtClockSystem.toString(): " + zdtClockSystem );
System.out.println ( "zdtClockSystemDefaultZone.toString(): " + zdtClockSystemDefaultZone );

America/Los_Angeles was the JVM current default zone on the computer that ran this code.

zdtClockSystemUTC.toString(): 2016-12-31T20:52:39.688Z

zdtClockSystem.toString(): 2016-12-31T15:52:39.750-05:00[America/Montreal]

zdtClockSystemDefaultZone.toString(): 2016-12-31T12:52:39.762-08:00[America/Los_Angeles]

The Instant class is always in UTC by definition. So these three zone-related Clock usages have exactly the same effect.

Instant instantClockSystemUTC = Instant.now ( Clock.systemUTC () );
Instant instantClockSystem = Instant.now ( Clock.system ( ZoneId.of ( "America/Montreal" ) ) );
Instant instantClockSystemDefaultZone = Instant.now ( Clock.systemDefaultZone () );

instantClockSystemUTC.toString(): 2016-12-31T20:52:39.763Z

instantClockSystem.toString(): 2016-12-31T20:52:39.763Z

instantClockSystemDefaultZone.toString(): 2016-12-31T20:52:39.763Z

Default clock

The implementation used by default for Instant.now is the one returned by Clock.systemUTC(). This is the implementation used when you do not specify a Clock. See for yourself in pre-release Java 9 source code for Instant.now.

public static Instant now() {
    return Clock.systemUTC().instant();
}

The default Clock for OffsetDateTime.now and ZonedDateTime.now is Clock.systemDefaultZone(). See source code.

public static ZonedDateTime now() {
    return now(Clock.systemDefaultZone());
}

The behavior of the default implementations changed between Java 8 and Java 9. In Java 8, the current moment is captured with a resolution only in milliseconds despite the classes’ ability to store a resolution of nanoseconds. Java 9 brings a new implementation able to capture the current moment with a resolution of nanoseconds – depending, of course, on the capability of your computer hardware clock.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes. Hibernate 5 & JPA 2.2 support java.time.

Where to obtain the java.time classes?

Bighorn answered 26/10, 2014 at 7:54 Comment(0)
O
43

As said by Jon Skeet:

"use Joda Time" is almost always the best answer to any question involving "how do I achieve X with java.util.Date/Calendar?"

So here goes (presuming you've just replaced all your new Date() with new DateTime().toDate())

//Change to specific time
DateTimeUtils.setCurrentMillisFixed(millis);
//or set the clock to be a difference from system time
DateTimeUtils.setCurrentMillisOffset(millis);
//Reset to system time
DateTimeUtils.setCurrentMillisSystem();

If you want import a library that has an interface (see Jon's comment below), you could just use Prevayler's Clock, which will provide implementations as well as the standard interface. The full jar is only 96kB, so it shouldn't break the bank...

Oblong answered 4/1, 2010 at 19:48 Comment(6)
No, I wouldn't recommend this - it doesn't move you towards a genuinely replaceable clock; it keeps you using statics throughout. Using Joda Time is of course a good idea, but I'd still want to go with a Clock interface or similar.Integration
@Jon: True - for testing, it's ok, but it's better to have an interface.Oblong
@Jon: This is a smooth and IMHO perfect and easy way if you want to test time dependent code which already uses JodaTime. Trying to reinvent the wheel by introducing yet another abstraction layer/framework is not the way to go, if everything was solved for you with JodaTime already.Delight
@StefanHaberl: You don't need to reinvent the wheel. You only need to introduce a single extra interface + implementations, and you can still use Joda Time everywhere, just avoiding the constructors which infer the current date/time.Integration
@JonSkeet: This is not what Mike was asking for originally. He wanted a tool to test time dependent code and not a full-blown replaceable clock in production code. I prefer keeping my architecture as complex as needed but as simple as possible. Introducing an additional layer of abstraction (i.e. interface) here is simply not necessaryDelight
@StefanHaberl: There are many things which aren't strictly "necessary" - but really what I'm suggesting is making an already-present dependency explicit and more easily testable in isolation. Faking the system time with statics has all the normal problems of statics - it's global state for no good reason. Mike was asking for a way to write testable code which used the current date and time. I still believe my suggestion is far cleaner (and makes dependencies clearer) than effectively fudging a static.Integration
R
16

While using some DateFactory pattern seems nice, it does not cover libraries you can't control - imagine Validation annotation @Past with implementation relying on System.currentTimeMillis (there is such).

That's why we use jmockit to mock the system time directly:

import mockit.Mock;
import mockit.MockClass;
...
@MockClass(realClass = System.class)
public static class SystemMock {
    /**
     * Fake current time millis returns value modified by required offset.
     *
     * @return fake "current" millis
     */
    @Mock
    public static long currentTimeMillis() {
        return INIT_MILLIS + offset + millisSinceClassInit();
    }
}

Mockit.setUpMock(SystemMock.class);

Because it's not possible to get to the original unmocked value of millis, we use nano timer instead - this is not related to wall clock, but relative time suffices here:

// runs before the mock is applied
private static final long INIT_MILLIS = System.currentTimeMillis();
private static final long INIT_NANOS = System.nanoTime();

private static long millisSinceClassInit() {
    return (System.nanoTime() - INIT_NANOS) / 1000000;
}

There is documented problem, that with HotSpot the time gets back to normal after a number of calls - here is the issue report: http://code.google.com/p/jmockit/issues/detail?id=43

To overcome this we have to turn on one specific HotSpot optimization - run JVM with this argument -XX:-Inline.

While this may not be perfect for production, it is just fine for tests and it is absolutely transparent for application, especially when DataFactory doesn't make business sense and is introduced only because of tests. It would be nice to have built-in JVM option to run in different time, too bad it is not possible without hacks like this.

Complete story is in my blog post here: http://virgo47.wordpress.com/2012/06/22/changing-system-time-in-java/

Complete handy class SystemTimeShifter is provided in the post. Class can be used in your tests, or it can be used as the first main class before your real main class very easily in order to run your application (or even whole appserver) in a different time. Of course, this is intented for testing purposes mainly, not for production environment.

EDIT July 2014: JMockit changed a lot lately and you are bound to use JMockit 1.0 to use this correctly (IIRC). Definitely can't upgrade to newest version where interface is completly different. I was thinking about inlining just the necessary stuff, but as we don't need this thing in our new projects I'm not developing this thing at all.

Rosalbarosalee answered 2/1, 2013 at 10:5 Comment(2)
The fact that you're changing the time for every class goes both ways... for instance if you'd mock nanoTime instead of currentTimeMillis you'd have to be very careful not to break java.util.concurrent. As a rule of thumb, if a 3rd party library is hard to use in tests, you shouldn't mock the inputs of the library: you should mock the library itself.Stelliform
We use this technique for tests where we don't mock anything else. It is integration test and we test the whole stack whether it does what it should when some activity happens the first time in year, etc. Good point about nanoTime, luckily we don't need to mock that one as it has no wall-clock meaning at all. I'd love to see Java with time shifting features as I needed them more than once already and messing with system time because of this is very unlucky.Rosalbarosalee
B
8

Powermock works great. Just used it to mock System.currentTimeMillis().

Brittani answered 3/4, 2010 at 18:27 Comment(4)
I tried to use Powermock, and if I understood it correctly it wouldn't really mock callee side. Instead it finds all callers and than applies the mock. This may be extremely time consuming as you have to let it check EVERY class in your classpath if you want to be realy really sure your application works in shifted time. Correct me if I'm wrong about Powermock way of mocking.Rosalbarosalee
@Rosalbarosalee No, you understood it wrong. PowerMock will never "check every class in your classpath"; it will only check the classes that you specifically tell it to check (through the @PrepareForTest annotation on the test class).Caleb
@Rogério - that's exactly how I understand it - I said "you have to let it...". That is if you expect call to System.currentTimeMillis anywhere on your classpath (any lib) you have to check every class. That's what I meant, nothing else. The point is that you mock that behavior on caller side ("you specifically tell"). This is OK for simple test but not for tests where you can't be sure what calls that method from where (e.g. more complex component tests with libraries involved). That does not mean Powermock is wrong at all, it just means that you can't use it for this type of test.Rosalbarosalee
@Rosalbarosalee Yes, I see what you mean. I thought you meant that PowerMock would have to automatically examine every class in the classpath, which obviously would be absurd. In practice, though, for unit tests we usually know which class(es) reads the time, so it's not an issue to specify it; for integration tests, indeed, the requirement in PowerMock to have all using classes specified may be a problem.Caleb
C
6

Use Aspect-Oriented Programming (AOP, for example AspectJ) to weave the System class to return a predefined value which you could set within your test cases.

Or weave the application classes to redirect the call to System.currentTimeMillis() or to new Date() to another utility class of your own.

Weaving system classes (java.lang.*) is however a little bit more trickier and you might need to perform offline weaving for rt.jar and use a separate JDK/rt.jar for your tests.

It's called Binary weaving and there are also special tools to perform weaving of System classes and circumvent some problems with that (e.g. bootstrapping the VM may not work)

Cumshaw answered 4/1, 2010 at 19:49 Comment(1)
This works, of course, but it's much easier to do with a mocking tool than with an AOP tool; the latter kind of tool is just too generic for the purpose in question.Caleb
C
6

A working way to override current system time for JUnit testing purposes in a Java 8 web application with EasyMock, without Joda Time, and without PowerMock.

Here's what you need to do:

What needs to be done in the tested class

Step 1

Add a new java.time.Clock attribute to the tested class MyService and make sure the new attribute will be initialized properly at default values with an instantiation block or a constructor:

import java.time.Clock;
import java.time.LocalDateTime;

public class MyService {
  // (...)
  private Clock clock;
  public Clock getClock() { return clock; }
  public void setClock(Clock newClock) { clock = newClock; }

  public void initDefaultClock() {
    setClock(
      Clock.system(
        Clock.systemDefaultZone().getZone() 
        // You can just as well use
        // java.util.TimeZone.getDefault().toZoneId() instead
      )
    );
  }
  { 
    initDefaultClock(); // initialisation in an instantiation block, but 
                        // it can be done in a constructor just as well
  }
  // (...)
}

Step 2

Inject the new attribute clock into the method which calls for a current date-time. For instance, in my case I had to perform a check of whether a date stored in dataase happened before LocalDateTime.now(), which I remplaced with LocalDateTime.now(clock), like so:

import java.time.Clock;
import java.time.LocalDateTime;

public class MyService {
  // (...)
  protected void doExecute() {
    LocalDateTime dateToBeCompared = someLogic.whichReturns().aDate().fromDB();
    while (dateToBeCompared.isBefore(LocalDateTime.now(clock))) {
      someOtherLogic();
    }
  }
  // (...) 
}

What needs to be done in the test class

Step 3

In the test class, create a mock clock object and inject it into the tested class's instance just before you call the tested method doExecute(), then reset it back right afterwards, like so:

import java.time.Clock;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import org.junit.Test;

public class MyServiceTest {
  // (...)
  private int year = 2017;
  private int month = 2;
  private int day = 3;

  @Test
  public void doExecuteTest() throws Exception {
    // (...) EasyMock stuff like mock(..), expect(..), replay(..) and whatnot

    MyService myService = new MyService();
    Clock mockClock =
      Clock.fixed(
        LocalDateTime.of(year, month, day, 0, 0).toInstant(OffsetDateTime.now().getOffset()),
        Clock.systemDefaultZone().getZone() // or java.util.TimeZone.getDefault().toZoneId()
      );
    myService.setClock(mockClock); // set it before calling the tested method

    myService.doExecute(); // calling tested method 

    myService.initDefaultClock(); // reset the clock to default right afterwards with our own previously created method

    // (...) remaining EasyMock stuff: verify(..) and assertEquals(..)
    }
  }

Check it in debug mode and you will see the date of 2017 Feb 3 has been correctly injected into myService instance and used in the comparison instruction, and then has been properly reset to current date with initDefaultClock().

Cotyledon answered 21/8, 2017 at 14:46 Comment(1)
Thanks for the examples. IMO, this should be the accepted answer.Drees
O
3

In my opinion only a none-invasive solution can work. Especially if you have external libs and a big legacy code base there is no reliable way to mock out time.

JMockit ... works only for restricted number of times

PowerMock & Co ...needs to mock the clients to System.currentTimeMillis(). Again an invasive option.

From this I only see the mentioned javaagent or aop approach being transparent to the whole system. Has anybody done that and could point to such a solution?

@jarnbjo: could you show some of the javaagent code please?

Ogata answered 23/6, 2012 at 22:46 Comment(1)
jmockit solution working for unlimited number of times with a little -XX:-Inline hack (on Sun's HotSpot): virgo47.wordpress.com/2012/06/22/changing-system-time-in-java Complete handy class SystemTimeShifter is provided in the post. Class can be used in your tests, or it can be used as the first main class before your real main class very easily in order to run your application (or even whole appserver) in a different time. Of course, this is intented for testing purposes mainly, not for production environment.Rosalbarosalee
S
3

Here is an example using PowerMockito. Also there is an example with new Date().
More details about mocking system classes.

@RunWith(PowerMockRunner.class)
@PrepareForTest(LegacyClass.class)
public class SystemTimeTest {
    
    private final Date fakeNow = Date.from(Instant.parse("2010-12-03T10:15:30.00Z"));

    @Before
    public void init() {
        PowerMockito.mockStatic(System.class);
        PowerMockito.when(System.currentTimeMillis()).thenReturn(fakeNow.getTime());
        System.out.println("Fake currentTimeMillis: " + System.currentTimeMillis());
    }

    @Test
    public void legacyClass() {
        new LegacyClass().methodWithCurrentTimeMillis();
    }

}

Some legacy class you are testing:

class LegacyClass {

    public void methodWithCurrentTimeMillis() {
        long now = System.currentTimeMillis();
        System.out.println("LegacyClass System.currentTimeMillis() is " + now);
    }

}

Console output

Fake currentTimeMillis: 1291371330000
LegacyClass System.currentTimeMillis() is 1291371330000
Stannite answered 2/4, 2021 at 14:28 Comment(0)
H
2

There really isn't a way to do this directly in the VM, but you could all something to programmatically set the system time on the test machine. Most (all?) OS have command line commands to do this.

Harkness answered 4/1, 2010 at 19:43 Comment(2)
For example, on windows the date and time commands. On Linux the date command.Harkness
Why shouldn't it be possible to do this with AOP or instrumentalization (been there done that)?Departmentalism
K
2

If you're running Linux, you can use the master branch of libfaketime, or at the time of testing commit 4ce2835.

Simply set the environment variable with the time you'd like to mock your java application with, and run it using ld-preloading:

# bash
export FAKETIME="1985-10-26 01:21:00"
export DONT_FAKE_MONOTONIC=1
LD_PRELOAD=/usr/local/lib/faketime/libfaketimeMT.so.1 java -jar myapp.jar

The second environment variable is paramount for java applications, which otherwise would freeze. It requires the master branch of libfaketime at the time of writing.

If you'd like to change the time of a systemd managed service, just add the following to your unit file overrides, e.g. for elasticsearch this would be /etc/systemd/system/elasticsearch.service.d/override.conf:

[Service]
Environment="FAKETIME=2017-10-31 23:00:00"
Environment="DONT_FAKE_MONOTONIC=1"
Environment="LD_PRELOAD=/usr/local/lib/faketime/libfaketimeMT.so.1"

Don't forget to reload systemd using `systemctl daemon-reload

Kwasi answered 3/11, 2017 at 9:41 Comment(0)
S
-1

If you want to mock the method having System.currentTimeMillis() argument then you can pass anyLong() of Matchers class as an argument.

P.S. I am able to run my test case successfully using the above trick and just to share more details about my test that I am using PowerMock and Mockito frameworks.

Sybil answered 12/8, 2015 at 10:12 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.