13

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
  • 43,537
  • 11
  • 94
  • 122
iberbeu
  • 15,295
  • 5
  • 27
  • 48
  • 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

4 Answers4

24

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,421,763
  • 867
  • 9,128
  • 9,194
  • 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
  • 43,537
  • 11
  • 94
  • 122
  • 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
2

IMO you should never create a wrapper class just for the sake of testing. You can just test that your date field is not null and trust the library LocalDateTime.now() if you really want to test it you can:

assertThat(myObj.getDate())isCloseTo(now()); 

from assertJ https://assertj.github.io/doc/#assertj-core-3.18.0-isCloseTo-Duration

Opri
  • 590
  • 2
  • 11
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");