12

I have this class

class MyObject {
    private LocalDateTime date;

    public LocalDateTime getDate() { return this.date; }

    public void myMethod() {
        this.date = LocalDateTime.now();
    }
}

How can I test that the date is properly set? I cannot mock now() because it is static and if I used LocalDateTime in the test both dates won't be the same.

Nicolas Filotto
  • 41,524
  • 11
  • 90
  • 115
iberbeu
  • 13,819
  • 5
  • 26
  • 47
  • 1
    This might help you: http://stackoverflow.com/questions/21105403/mocking-static-methods-with-mockito – mtj Sep 16 '16 at 09:17
  • Yeah, I guess you are right. I didn't want to use powermockito though... Is there any other way? – iberbeu Sep 16 '16 at 09:19

3 Answers3

21

I cannot mock now() because it is static

Indeed - but fortunately, you don't have to. Instead, consider "a date/time provider" as a dependency, and inject that as normal. java.time provides just such a dependency: java.time.Clock. In tests you can provide a fixed clock via Clock.fixed(...) (no mocking required) and for production you'd use Clock.system(...).

Then you change your code to something like:

class MyObject {
    private final Clock clock;
    private LocalDateTime date;

    public MyObject(Clock clock) {
        this.clock = clock;
    }

    public LocalDateTime getDate() {
        return this.date;
    }

    public void myMethod() {
        this.date = LocalDateTime.now(clock);
    }
}

... or however you normally deal with dependencies.

Jon Skeet
  • 1,335,956
  • 823
  • 8,931
  • 9,049
  • This sounds good, even though we need to introduce a clock that actually is not really needed – iberbeu Sep 16 '16 at 10:24
  • 1
    @iberbeu: Well, it depends on your definition of "needed" - I'd say it's "needed to clearly express the dependency". You could introduce your own app-specific singleton and always use that if you really want, but personally I prefer to make it clear where I'm going to rely on the current time. – Jon Skeet Sep 16 '16 at 11:16
  • I know Jon. I could've accepted both answers but I had to choose... Let's see what the rest of the users find better – iberbeu Sep 19 '16 at 07:04
  • 4
    @iberbeu: The accepted answer will fail if the test occurs at the point of a daylight saving time change or if the system clock changes. I prefer to isolate my tests from the system clock entirely - and make the dependency on a clock clear. – Jon Skeet Sep 19 '16 at 07:24
7

You could generate a date time just before calling myMethod() and make sure that this date is before or equals to the date returned by getDate(), something like that:

@Test
public void testDate() {
    MyObject object = new MyObject();
    // Get the current date time 
    LocalDateTime time = LocalDateTime.now();
    // Affect the current date time to the field date
    object.myMethod();
    // Make sure that it is before or equals
    Assert.assertTrue(time.isBefore(object.getDate()) || time.isEqual(object.getDate()));
}

If you don't care adding coupling to your class a better approach could be to provide a Supplier<LocalDateTime> to your class as next:

public class MyObject {
    private final Supplier<LocalDateTime> supplier;
    private LocalDateTime date;

    public MyObject() {
        this(LocalDateTime::now);
    }

    public MyObject(final Supplier<LocalDateTime> supplier) {
        this.supplier = supplier;
    }

    public LocalDateTime getDate() { return this.date; }

    public void myMethod() {
        this.date = supplier.get();
    }
}

This way it will be easy to create a Supplier for testing purpose in your test case.

For example the test case could then be:

@Test
public void testDate() {
    LocalDateTime time = LocalDateTime.now();
    MyObject object = new MyObject(() -> time);
    object.myMethod();
    Assert.assertTrue(time.isEqual(object.getDate()));
}
Nicolas Filotto
  • 41,524
  • 11
  • 90
  • 115
  • 1
    This approach is easy to understand and to implement and it works. Anyway I think it is not exact enough... For some cases this will be the way to go but generally I prefer Jon's answer – iberbeu Sep 16 '16 at 10:21
  • I understand it is indeed a good approach even if I personally don't like the idea to add coupling to your class just for a simple test case – Nicolas Filotto Sep 16 '16 at 10:25
  • I agree on that! To introduce a clock is debatable. As I said, I will use your approach quite often, but as an answer I think Jon's one is more general and precise... – iberbeu Sep 16 '16 at 10:28
  • check my response update, it is much more acceptable to me to use a supplier instead as it is much more generic – Nicolas Filotto Sep 16 '16 at 10:31
0

Update your class as below :

class MyObject {
    private LocalDateTime date;

    public Supplier<LocalDateTime> localDateTime = ()-> LocalDateTime.now();

    public LocalDateTime getDate() { return this.date; }

    public void myMethod() {
        this.date = localDateTime.get();
    }
}

and in the test method update the supplier variable as below just before calling myMethod:

objMyObject.localDateTime = ()-> LocalDateTime.parse("2020-11-24T23:59:59.999");