Unit testing time driven methods
Tags: java, testing
Published: 2021-10-23 18:49:44

Hi! This is my first post on a series of quick tips to improve your tests in Java, I hope you will learn something today 👍

Have you ever used `Instant.now()`, `LocalDateTime.now()` or `ZonedDateTime.now()` in your code, only to find it difficult to test later on ?

Let's take an example :
```java
public class Hohoho {

    public String isItChristmasYet() {
        LocalDate now = LocalDate.now();
        
        if (now.getMonth() == Month.DECEMBER) {
            if (now.getDayOfMonth() == 25) {
                return "Yes! Merry Christmas! 🎅";
            }
            else if (now.getDayOfMonth() == 24) {
                return "Almost there! 😁";
            }
        }
        return "Not yet 😔";
    }
}
```

How would you test this method?

You could pass the LocalDate as an argument of the method but that would change the semantic of the method.

You could also use some libs like Powermockito to mock the static method of `LocalDate.now()` but this would also affect other tests.

Here is a tip : use a **[java.time.Clock](https://docs.oracle.com/javase/8/docs/api/java/time/Clock.html)**!

Every time you need to use a *.now() method, pass a Clock object as a parameter.
You can set the clock to be the system default in you constructor, and add a setter method to override it during tests.

Let's apply this in our example :
```java
public class Hohoho {
    
    private Clock clock;

    public Hohoho() {
        // Set a normal value for the clock, here UTC
        this.clock = Clock.systemUTC();
    }

    // For testing purpose : override the clock
    public void setClock(Clock clock) {
        this.clock = clock;
    }

    public String isItChristmasYet() {
        LocalDate now = LocalDate.now(clock); // <- use the clock

        if (now.getMonth() == Month.DECEMBER) {
            if (now.getDayOfMonth() == 25) {
                return "Yes! Merry Christmas! 🎅";
            }
            else if (now.getDayOfMonth() == 24) {
                return "Almost there! 😁";
            }
        }
        return "Not yet 😔";
    }
}
```

If you want to avoid the setter, you could inject the Clock through dependency injection.
I personally don't mind having a setter in this case.

Now let's look how this will help our tests :

```java
class HohohoTest {

    private Hohoho underTest = new Hohoho();

    @DisplayName("Should respond 'Not Yet' in February")
    @Test
    void februaryTest() {
        // Fix the clock to my birthday
        Clock clock = Clock.fixed(Instant.parse("2022-02-21T12:00:00Z"), ZoneId.of("UTC"));
        underTest.setClock(clock);

        String actual = underTest.isItChristmasYet();

        assertThat(actual).isEqualTo("Not yet 😔");
    }

    @DisplayName("Should respond 'Almost there' the 24th of December")
    @Test
    void december24thTest() {
        // Fix the clock to christmas eve
        Clock clock = Clock.fixed(Instant.parse("2022-12-24T12:00:00Z"), ZoneId.of("UTC"));
        underTest.setClock(clock);

        String actual = underTest.isItChristmasYet();

        assertThat(actual).isEqualTo("Almost there! 😁");
    }

    @DisplayName("Should respond 'Yes' on Christmas")
    @Test
    void christmasTest() {
        // Fix the clock to christmas day
        Clock clock = Clock.fixed(Instant.parse("2022-12-25T12:00:00Z"), ZoneId.of("UTC"));
        underTest.setClock(clock);

        String actual = underTest.isItChristmasYet();

        assertThat(actual).isEqualTo("Yes! Merry Christmas! 🎅");
    }

}
```
By fixing the Clock object to a specific instant in time, every method now() that use the clock will return exactly the same result.

This means your test will always be consistent, using the same values every time, no mater *when* they are run : be it on new year's eve, 28th of February or while switching from winter time to summer time!


Do you have another approach for testing time sensitive methods? Let me know in the comments! 👍