Index

Testing with Time

Java Date and Time

20.1 Faking the Current Time with Clock

When writing tests that depend on the current date and time, relying on the system clock (Instant.now(), LocalDateTime.now(), etc.) can lead to flaky and non-deterministic tests. The exact moment the test runs affects the output, making debugging difficult and tests unreliable.

To address this, Java’s java.time API introduces the Clock class, which provides a way to control and manipulate the current time during testing. By injecting a controllable Clock instance, you can simulate any moment in time and create repeatable, deterministic tests.

Concept of Controllable Clocks

A Clock is an abstraction of the current time source. Instead of calling static methods like Instant.now(), you call Instant.now(clock). The clock instance decides what “now” means:

Using Clock.fixed() for Repeatable Tests

Clock.fixed() creates a clock that always returns the same instant:

import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;

public class FixedClockExample {
    public static void main(String[] args) {
        Instant fixedInstant = Instant.parse("2025-06-22T10:00:00Z");
        Clock fixedClock = Clock.fixed(fixedInstant, ZoneId.of("UTC"));

        // Now calls using this clock always return the fixed instant
        Instant now = Instant.now(fixedClock);
        System.out.println(now);  // Output: 2025-06-22T10:00:00Z
    }
}

In tests, injecting this fixed clock allows you to simulate an exact moment in time, ensuring that tests relying on "now" behave consistently every run.

Using Clock.offset() for Simulated Time Shifts

Clock.offset() creates a clock that advances or rewinds time relative to another clock by a fixed duration:

import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;

public class OffsetClockExample {
    public static void main(String[] args) throws InterruptedException {
        Clock baseClock = Clock.systemUTC();
        Clock offsetClock = Clock.offset(baseClock, Duration.ofHours(2));

        Instant baseNow = Instant.now(baseClock);
        Instant offsetNow = Instant.now(offsetClock);

        System.out.println("Base time:   " + baseNow);
        System.out.println("Offset time: " + offsetNow);
    }
}

This is useful to simulate scenarios like future or past times, or clocks with time drift.

Benefits for Testing

Summary

Using Clock.fixed() and Clock.offset() to fake the current time in your tests provides a clean, built-in solution for controlling temporal behavior. It ensures your tests are stable, repeatable, and easier to maintain — a best practice for any application that depends on date and time.

Index

20.2 Time-based Unit Tests and Assertions

Testing time-dependent logic can be challenging because system time constantly changes. Without control over the clock, tests can become flaky, intermittent, and difficult to debug. To build reliable unit tests, you need to isolate the time component and use precise assertions involving Instant, Duration, and related classes.

Example 1: Asserting Elapsed Time Using Duration

Suppose you have a method that records the start and end time of a task, and you want to assert that the elapsed time is within expected bounds.

import static org.junit.jupiter.api.Assertions.assertTrue;

import java.time.Duration;
import java.time.Instant;

import org.junit.jupiter.api.Test;

public class ElapsedTimeTest {

    @Test
    public void testElapsedTimeWithinLimit() {
        Instant start = Instant.parse("2025-06-22T10:00:00Z");
        Instant end = Instant.parse("2025-06-22T10:00:05Z");

        Duration elapsed = Duration.between(start, end);

        // Assert that elapsed time is less than 10 seconds
        assertTrue(elapsed.getSeconds() < 10, "Elapsed time should be under 10 seconds");
    }
}

This test is deterministic because the instants are fixed and known.

Example 2: Verifying Event Ordering with Instant

When your system processes events, you may want to confirm the order of timestamps.

import static org.junit.jupiter.api.Assertions.assertTrue;

import java.time.Instant;

import org.junit.jupiter.api.Test;

public class EventOrderTest {

    @Test
    public void testEventOrder() {
        Instant firstEvent = Instant.parse("2025-06-22T08:00:00Z");
        Instant secondEvent = Instant.parse("2025-06-22T08:05:00Z");

        assertTrue(firstEvent.isBefore(secondEvent), "First event must occur before second event");
    }
}

Using isBefore(), isAfter(), and isEqual() methods helps write clear and meaningful assertions about temporal order.

Example 3: Using Clock to Test Time-Sensitive Methods

Suppose you have a method that depends on the current time to decide behavior (e.g., whether a deadline has passed). Injecting a controllable Clock makes tests repeatable:

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;

import org.junit.jupiter.api.Test;

public class DeadlineCheckerTest {

    private boolean isDeadlinePassed(Instant deadline, Clock clock) {
        Instant now = Instant.now(clock);
        return now.isAfter(deadline);
    }

    @Test
    public void testDeadlineNotPassed() {
        Instant deadline = Instant.parse("2025-06-22T12:00:00Z");
        Clock fixedClock = Clock.fixed(Instant.parse("2025-06-22T11:00:00Z"), ZoneId.of("UTC"));

        assertFalse(isDeadlinePassed(deadline, fixedClock));
    }

    @Test
    public void testDeadlinePassed() {
        Instant deadline = Instant.parse("2025-06-22T12:00:00Z");
        Clock fixedClock = Clock.fixed(Instant.parse("2025-06-22T13:00:00Z"), ZoneId.of("UTC"));

        assertTrue(isDeadlinePassed(deadline, fixedClock));
    }
}

Common Pitfalls and How to Avoid Them

Summary

Using fixed or controlled Instant values, Duration calculations, and injected Clock instances helps you write robust, deterministic unit tests for time-based logic. By carefully asserting durations and event order, and avoiding system time dependence, your tests become reliable and maintainable.

Index

20.3 Using Libraries like JUnit and Mockito with Clock

Testing time-dependent code becomes much easier and more reliable when you use Clock injection combined with popular testing frameworks like JUnit and Mockito. By injecting a controllable Clock into your classes, you can mock or fix the current time, enabling deterministic tests that do not depend on the real system clock.

Injecting Clock in Your Service Class

First, design your classes to accept a Clock instance instead of calling Instant.now() or LocalDateTime.now() directly. This allows you to inject a fixed or mock clock during tests.

import java.time.Clock;
import java.time.Instant;

public class TimeSensitiveService {
    private final Clock clock;

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

    public Instant getCurrentInstant() {
        return Instant.now(clock);
    }

    // Other methods using clock for current time...
}

Using JUnit with a Fixed Clock

You can provide a fixed clock in your unit tests to simulate a specific moment in time:

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;

import org.junit.jupiter.api.Test;

public class TimeSensitiveServiceTest {

    @Test
    public void testGetCurrentInstant_fixedClock() {
        Instant fixedInstant = Instant.parse("2025-06-22T10:00:00Z");
        Clock fixedClock = Clock.fixed(fixedInstant, ZoneId.of("UTC"));
        TimeSensitiveService service = new TimeSensitiveService(fixedClock);

        assertEquals(fixedInstant, service.getCurrentInstant());
    }
}

Here, Clock.fixed() guarantees that every call to Instant.now(clock) returns the same instant, making assertions straightforward and repeatable.

Mocking Clock with Mockito

Mockito allows you to mock the behavior of Clock dynamically, useful for testing code that relies on advancing time or variable timestamps.

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;

import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;

import org.junit.jupiter.api.Test;

public class TimeSensitiveServiceMockitoTest {

    @Test
    public void testGetCurrentInstant_mockClock() {
        Clock mockClock = mock(Clock.class);
        Instant now = Instant.parse("2025-06-22T10:00:00Z");
        when(mockClock.instant()).thenReturn(now);
        when(mockClock.getZone()).thenReturn(ZoneId.of("UTC"));

        TimeSensitiveService service = new TimeSensitiveService(mockClock);

        assertEquals(now, service.getCurrentInstant());

        // Simulate time advancing
        Instant later = now.plusSeconds(60);
        when(mockClock.instant()).thenReturn(later);

        assertEquals(later, service.getCurrentInstant());
    }
}

In this example, the mock Clock returns a predefined instant, and you can dynamically change what it returns to simulate time progression within a test.

Benefits of Using Clock with Testing Frameworks

Summary

By combining Clock injection with JUnit and Mockito, you gain precise control over the flow of time in your tests. This approach drastically improves test reliability, makes assertions straightforward, and supports advanced scenarios like simulating time passing. Adopting these practices leads to cleaner, more maintainable, and robust time-based test suites.

Index