The one that implements a Clock for deterministic tests

Is this thing on?! Hello World. It sounds about right to discuss naming in the first post. In software engineering naming is considered to be hard. So read on to learn how the name Bit More came to...

Is this thing on?! Hello World. It sounds about right to discuss naming in the first post. In software engineering naming is considered to be hard. So read on to learn how the name Bit More came to be. But first, let’s do some coding, shall we?!

One of these days I was facing some code - more about that in another post - that was reliant on the passing of time and in dire need of some tests thrown at. Java has a fairly straightforward way of representing time, namely ZonedTime and LocalDateTime. For simplicity’s sake, let’s focus on LocalDateTime. As long as your code deals with logic in the present all you need is LocalDateTime::now to get you the current date and time represented in the default time zone of the system.

Upon closer look, the now method comes in two flavors. One takes no arguments, the other takes a Clock. The former will use the default clock of the system in the background. A clock can give you the current instant. What current means depends on the clock you instantiate.

There are three options to choose from. We will see in a bit that neither perfectly suits our needs for testing. See, testing anything that depends on the passing of time becomes tricky quicker than the blink of an eye. A bulletproof solution is a challenge to say the least.

Vanilla Flavor - System Clock🔋

Flavor zero: Clock::systemUTC or Clock::systemDefaultZone represents a regular clock with its hands set at the current time, happily ticking as one expects clocks to be ticking.

Flavor One - Fixed Clock 🔋🚫

Flavor one: Clock::fixed represents a clock with its batteries out. You can set the hands to any hour, minute and second but the clock will be frozen in time. Any call to the Clock::instant method will yield the same point in time that you set during instantiation.

Flavor Two - Offset Clock 🕕

Flavor two: Clock::offset takes inspiration from the previous two. You can set the hands and the clock will be ticking starting from that epoch you set. Let’s see this in action:

LocalDateTime epoch = LocalDateTime.of(1983, 4, 10, 12, 30);
Clock baseClock = Clock.systemUTC();
Duration offset = Duration.between(LocalDateTime.now(clock), epoch);
Clock clock = Clock.offset(baseClock, epoch);

In the snippet above, clock represents a clock that started ticking on the 10th of April in 1983 at 12:30pm. Injecting this clock into LocalDateTime would yield the epoch value:

LocalDateTime timeIn1983 = LocalDateTime.now(clock);

Call it 10 seconds later, it would yield the instant that is 10 seconds later but still in 1983.

 // Loiter for 10 seconds
 LocalDateTime tenSecondsLaterIn1983 = LocalDateTime.now(clock);

Apart from being able to wind this clock back into the past or forward into the future - or back to the future 😉 -, it will tick as you expect a regular clock to tick - one every second.

Building upon the offset clock, we can create a clock that runs in the past or in the future, but with an interval that is different from the regular one second.

Clock fastClockIn1983 = Clock.tick(offsetClock, Duration.ofSeconds(5));

This clock will be advancing 5 seconds with every tick while a regular clock would advance only 1 second. We can go crazy with the duration value and create clocks that leap hours, days or even months at a time.

Using Clock in tests

With these flavors, we have a handful of options to choose from for our testing needs. However, the problem is that the flavors that represent running clocks - with the batteries in -, we have no control over when those ticks happen. So the test code that we implement will end up using Thread.sleep to pass time or will be running a loop for a set period. Neither of these options are ideal. The former will obviously make our test run longer than needed:

for (int i = 0; i < 5; i++) { // Loop for 5 seconds
    // do some business logic
    Thread.sleep(1000); // Let one second pass
}

The latter will yield a nondeterministic results. The test will work most of the time, but every once in a while results might be flaky due to tiny nanosecond differences:

LocalDateTime until = LocalDateTime.now().plusSeconds(5);
while (LocalDateTime.now().isBefore(until)) { // Loop for at least 5 seconds
    // do some business logic
}

⏰ Toy Clock 🧸

For the ultimate test, we would like to have total control over our clock including when the ticks happen. We have seen that the options that ship with Java do not provide such a clock out of the box, so we will have to get our hands dirty: let’s try to create such a clock.

Did I mention naming is hard? I usually don’t land on the perfect name immediately, so I will start out with a placeholder name and see if during implementation something better pops up. I am gonna go with ToyClock.

public class ToyClock extends Clock {
     
    private final Clock baseClock;
    private final Duration tickInterval;
    private final AtomicLong tickCount = new AtomicLong(0);

    private TickingClock(LocalDateTime epoch, Duration tickInterval) {
        Clock fixedClock = Clock.fixed(epoch.toInstant(ZoneOffset.UTC), ZoneOffset.UTC);
        this.baseClock = Clock.offset(fixedClock, calculateOffset(fixedClock, epoch));
        this.tickInterval = tickInterval;
    }
     
}

In the snippet above, we are extending the abstract Clock to create our ToyClock class. For starters, lets give it three properties. A baseClock serving as the foundation of our implementation. It is a fixed clock with the batteries 🔋 out. A tickInterval to control how long or short the ticks are. And finally, a counter to keep track of how many times did our clock tick as it will be ticking only upon request.

To extend Clock we have to override the abstract methods:

@Override
public ZoneId getZone() {
    return baseClock.getZone();
}

@Override
public Clock withZone(ZoneId zone) {
    return baseClock.withZone(zone);
}

@Override
public Instant instant() {
    return baseClock.instant()
            .plus(tickInterval.multipliedBy(tickCount.incrementAndGet()));
}

Functions, getZone and withZone are just nuances, we have to override these. Lucky for us, the baseClock makes their implementation trivial. The real action happens in instant. Here we once again rely on baseClock to get the current instant - which is always the same, frozen in time - and add tickInterval x tickCount. Leveraging the AtomicLong class, we maintain thread safety of our implementation by incrementing and getting the counter value as an atomic operation. A side effect of a call to the instant method will be one tick of our ToyClock. Notice that tickInterval can be positive or negative, meaning that we can make our clock run forward or backward. But the real beauty is that peaking into the implementation of LocalDateTime::now we see this:

final Instant now = clock.instant();  // called once

So by injecting a ToyClock instance into LocalDateTime::now we can actually get a time value while also being able to control the ticking of our clock. A side effect of calling LocalDateTime::now is going to be exactly one tick of our clock. With this we can create test environments in which the passing of time us under tight control and yield deterministic outcomes, no matter how complex the time logic is. To demonstrate, let’s quickly impellent a unit test of ToyClock. To make it fun, let’s have it tick backwards by 5 minutes.

@Test
void tickBackward() {
    LocalDateTime epoch = LocalDateTime.now();
    int tickIntervalMinutes = -5;
    Clock clock = new ToyClock(epoch, Duration.ofMinutes(tickIntervalMinutes));
    for (int i = 0; i < 3; i++) {
        LocalDateTime now = LocalDateTime.now(clock); // calls Clock#internal that causes the tick
        assertEquals((i+1) * tickIntervalMinutes, now.getMinute());
    }
}

Inside the for loop, the clock ticks exactly once for a total of 3 ticks, so at the end our time will be 15 seconds in the past. It turns out that no better name popped up, so I am gonna stick with ToyClock for now. And that’s enough coding for one day.

If you read this far, now you must be really curious how the name of the site, Bit More, came about. The idea is actually my wife’s. She is not a software engineer, so I am not surprised that she is better at this than I ever hope to be.

The startup that I work for was looking for a name and I was discussing this with her during dinner one day. We started throwing out ideas while having toast that evening. The toaster had a button labelled ‘Bit more’ with a function of giving the bread an extra 30 seconds of heat. The domain of the startup is finance, so when my wife grabbed the ready toasts from the toaster she lit up with joy shouting ‘this is it’. I did enter the name into the company’s name selection pool, but ultimately ‘Bit More’ did not make the final round. That left my wife a tad bit sad as she was convinced this is a great name. So about a year went by and I told her that I feel the need to start writing some sort of a blog and that I want to write about code. Without hesitation she jumped up and said: you have to name it Bit More.

Hungry for more time? It so happens that a good friend of mine, Attila, is also into time. He is an avid collector of Seiko quartz watches. Take a look at his impressive collection here.