zoobzio December 31, 2023 Edit this page

Quick Start

This guide gets you from zero to deterministic time testing in minutes.

Installation

go get github.com/zoobz-io/clockz

Requires Go 1.24 or higher.

The Pattern

clockz works through dependency injection. Instead of calling time.Now() directly, your code accepts a Clock interface:

type Service struct {
    clock clockz.Clock
}

func NewService(clock clockz.Clock) *Service {
    return &Service{clock: clock}
}

In production, inject RealClock. In tests, inject FakeClock.

Production Usage

package main

import (
    "fmt"
    "time"

    "github.com/zoobz-io/clockz"
)

type Scheduler struct {
    clock clockz.Clock
}

func (s *Scheduler) NextRun() time.Time {
    return s.clock.Now().Add(24 * time.Hour)
}

func (s *Scheduler) WaitUntil(target time.Time) {
    duration := target.Sub(s.clock.Now())
    if duration > 0 {
        s.clock.Sleep(duration)
    }
}

func main() {
    scheduler := &Scheduler{clock: clockz.RealClock}

    next := scheduler.NextRun()
    fmt.Printf("Next run: %v\n", next)
}

Test Usage

func TestScheduler(t *testing.T) {
    // Start at a known time
    start := time.Date(2024, 6, 15, 10, 0, 0, 0, time.UTC)
    clock := clockz.NewFakeClockAt(start)

    scheduler := &Scheduler{clock: clock}

    // Verify next run is 24 hours later
    next := scheduler.NextRun()
    expected := start.Add(24 * time.Hour)

    if !next.Equal(expected) {
        t.Errorf("expected %v, got %v", expected, next)
    }

    // Test waiting (completes instantly)
    done := make(chan bool)
    go func() {
        scheduler.WaitUntil(next)
        done <- true
    }()

    // Advance time to trigger completion
    clock.Advance(24 * time.Hour)

    select {
    case <-done:
        // Success
    case <-time.After(100 * time.Millisecond):
        t.Fatal("WaitUntil did not complete")
    }
}

Key Points

  1. One interface, two implementationsClock is implemented by both RealClock and FakeClock
  2. Inject at construction — Pass the clock when creating your types
  3. Advance explicitlyFakeClock time only moves when you call Advance() or SetTime()
  4. Thread-safe — Both implementations are safe for concurrent use

Next Steps