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.
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:
Clock.fixed()
for Repeatable TestsClock.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.
Clock.offset()
for Simulated Time ShiftsClock.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.
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.
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.
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.
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.
Clock
to Test Time-Sensitive MethodsSuppose 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));
}
}
Flaky Tests Due to Real System Time: Calling Instant.now()
directly in production code and tests without control leads to non-repeatable tests that can pass or fail depending on when they run.
Test Dependencies on Time Zones: Avoid relying on the default system time zone in tests; instead, specify time zones explicitly to prevent unexpected behavior in different environments.
Ignoring Time Precision: Be mindful of nanoseconds vs milliseconds precision when comparing instants; inconsistencies can cause test failures.
Mixing Mutable and Immutable Time Objects: Always prefer immutable classes (Instant
, LocalDateTime
) over legacy mutable types to avoid concurrency and state issues in tests.
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.
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.
Clock
in Your Service ClassFirst, 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...
}
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.
Clock
with MockitoMockito 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.
Clock
with Testing FrameworksBy 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.