11

For some testing purposes I would like to predict exactly what System.currentTimeMillis() will return. Is there any way in which I can freeze or manually set what will return when System.currentTimeMillis() is called?

Raedwald
  • 46,613
  • 43
  • 151
  • 237
user22098
  • 173
  • 1
  • 2
  • 8

6 Answers6

28

I would strongly suggest that you avoid using System.currentTimeMillis (and new Date() etc) in your general code.

Instead, create a Clock interface representing "a service to give you the current time" and then create one implementation which does use System.currentTimeMillis or whatever, and a fake implementation that you can control explicitly.

Use dependency injection to make an instance of this service available to code which needs it. In production, use the System.currentTimeMillis version, and in testing use your fake.

This gives you the ability not just to stop time, but to set it to whatever you want - so you can have static test data which you know will never expire, and you can easily test tricky things around boundaries etc. I've used this approach very successfully in many projects, to the extent that in my Noda Time project it's the way of getting at "the current time".

Note that if you're doing any serious amount of time work in Java, I'd recommend using Joda Time, and making your Clock interface return an Instant:

public interface Clock {
    Instant now();
}
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 5
    On a related note: I'm really looking forward to [`java.time`](http://download.java.net/jdk8/docs/api/java/time/package-summary.html) (a.k.a [JSR-310](http://jcp.org/en/jsr/detail?id=310)) to finally arrive in Java 8. – Joachim Sauer Jul 01 '13 at 06:25
19

Yes, it is possible, but it is a test code smell.

You can use a mocking library which enables you to mock static methods (as in this PowerMock example), but you should avoid doing this, and encapsulate the time data as the other answers suggest.

This is how the test would look like, using PowerMock and Mockito:

@RunWith(PowerMockRunner.class)
@PrepareForTest(System.class)
public class TestTime {

    @Test
    public void testTime() {
        PowerMockito.mockStatic(System.class);
        PowerMockito.when(System.currentTimeMillis()).thenReturn(42l);
        System.out.println(System.currentTimeMillis()); //prints 42

        //your test code here
    }

}
mikołak
  • 9,605
  • 1
  • 48
  • 70
  • 4
    @PeterLawrey: No, that's a lower-case `L`, not a `1`. – Jon Skeet Jul 01 '13 at 06:27
  • 2
    :D. I *really* should finally retrain myself to use `L` instead. Oh well, let's call it a code-read-skill training bonus. – mikołak Jul 01 '13 at 06:28
  • 1
    You can actually mock *that* method? I'm amazed! – Stephen C Jul 01 '13 at 06:48
  • You can mock most methods with *PowerMock* (not sure about `native`). Of course, doesn't mean that you **should**, unless you're dealing with a legacy code monstrosity or something to that effect. – mikołak Jul 01 '13 at 08:48
2

Is it possible to freeze System.currentTimeMillis()? NO

You need to use a sort of wrapper around that time value if you want your code to be able to be tested

morgano
  • 17,210
  • 10
  • 45
  • 56
1

No, You cann't set or freeze System.currentTimeMills(). But if your requirement is something like that so in that case you can set time in variable and used when ever you want.but System.currentTimeMills() will always returns you the current time value in milliseconds.

Ashish Aggarwal
  • 3,018
  • 2
  • 23
  • 46
1

The new java.time package built into Java 8 includes a java.time.Clock interface "to allow alternate clocks to be plugged in as and when required. Use this instead.

del bao
  • 1,084
  • 1
  • 11
  • 20
-5

Yes, solution exists:

For testing purposes you may create your own wrapper System, for example

package my.pack;

import java.io.PrintStream;

public class System
{
    // reuse standard System.out:
    public static PrintStream out = java.lang.System.out;

    // do anything with chosen method
    public static long currentTimeMillis()
    {
        return 1234567;
    }

}

and import it into your class which you want to test

import my.pack.System;

In that case all calls to System will be passed through your own System class.

NOTE:

  • This is a solution for "how to intercept System.currentTimeMillis()"

  • This is NOT suitable for automatic testing

  • This is NOT an example of "how-to-design-a-good-program". If you ask for a good design, then you need to refactor your code, replace System.currentTimeMillis() - see other answers.

n00bot
  • 229
  • 1
  • 11
  • 5
    Are you suggesting to modify the source of the class under test *just during testing*? That would mean that the effective class you test is *not* the class you actually run in production! – Joachim Sauer Jul 01 '13 at 06:28
  • Yes, you need to modify your class for testing purposes (append one line), and yes, for production you need to remove that import. But yes, you CAN change behaviour, and this is an easy way – n00bot Jul 01 '13 at 06:35
  • Apart from the problem of testing a different class than what you're deploying, I also argue the "easy" way: automating this in a sane way would be a pain in the but. – Joachim Sauer Jul 01 '13 at 07:29
  • Sure, this solution is not for automatic testing. By "easy" I mean less refactor: you don't need to change all the lines with currentTimeMillis() wherever they are – n00bot Jul 01 '13 at 07:39