How to Mock time.Now in Go Tests

Replace time.Now with a variable function to inject fixed times in Go tests.

The clock breaks your test

You write a test for a function that checks if a token is expired. It passes on Monday morning. You run the suite again on Tuesday, and the test fails. The code hasn't changed. The clock has.

Flaky tests driven by time.Now() are a classic headache. The test depends on the exact second it runs, which makes it brittle and impossible to verify edge cases. You cannot test "what happens exactly at the deadline" if the deadline is always moving forward. You also cannot test expiration logic without waiting real seconds, which slows down the test suite.

Tests should be deterministic. If the clock breaks your test, the test is broken.

Inject the clock, not the time

Go doesn't have a built-in mock framework. You don't need one. The solution is simple dependency injection. Replace the direct call to time.Now() with a variable that holds a function. In production, that variable points to the real clock. In tests, you swap it for a function that returns whatever time you want.

This turns a hard dependency on the system clock into a soft dependency you can control. Go treats functions as first-class values. time.Now is a function, not a method call, so you can assign it to a variable, pass it around, and replace it. This is the foundation of the pattern.

Dependency injection turns side effects into inputs.

Minimal variable swap

Here's the pattern in its smallest form. A package-level variable holds the clock function. The production code calls the variable. The test overwrites the variable.

package main

import "time"

// nowFunc holds the current time function. Default is the real clock.
var nowFunc = time.Now

// CurrentTime returns the time from the injected clock.
func CurrentTime() time.Time {
    return nowFunc()
}

The variable nowFunc starts as time.Now. Any call to CurrentTime invokes nowFunc(), which resolves to the real system time. The code compiles and runs normally.

Here's the test that swaps the clock. It saves the original function, sets a fixed time, runs the check, and restores the original function.

package main

import (
    "testing"
    "time"
)

func TestCurrentTime(t *testing.T) {
    // Save the original function to restore it later.
    originalNow := nowFunc

    // Set a fixed time for the test.
    fixedTime := time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC)
    nowFunc = func() time.Time {
        return fixedTime
    }

    // Restore the original function when the test finishes.
    defer func() {
        nowFunc = originalNow
    }()

    got := CurrentTime()
    if !got.Equal(fixedTime) {
        t.Errorf("CurrentTime() = %v, want %v", got, fixedTime)
    }
}

The defer statement schedules a function call to run after the surrounding function returns. It's the standard way to handle cleanup in Go. Use it to restore global state so tests don't leak side effects. If the test panics, the deferred function still runs, preventing subsequent tests from inheriting the mock.

Global variables are a trap in parallel tests. Restore state or isolate it.

How the swap works at runtime

When the test calls nowFunc, the Go runtime looks up the variable in the package's data segment. If the test overwrote it, the runtime finds the closure. The closure captures fixedTime by reference. This means every call returns the same time.Time value.

The time.Time type carries timezone information. The Equal method compares wall-clock time while ignoring the timezone. Two times can represent the same instant but have different zones. Using == on time.Time values can fail unexpectedly if the zones differ. Always use Equal in assertions unless you specifically need to check the zone.

Convention: Public names start with a capital letter. Private names start lowercase. The variable nowFunc is unexported, so only code in the same package can access it. Tests in the same package can modify it. If you need to mock from a different package, you must export the variable or use a struct-based approach.

Realistic service with interface

Production code rarely just returns the time. It uses time to make decisions. Consider a service that checks if a session is active. Real code benefits from struct-based injection. This avoids global state and makes the dependency explicit.

Here's a service that accepts a clock interface. The interface defines the behavior the service needs. The service doesn't care about the implementation.

package service

import "time"

// Clock is the interface for time operations.
type Clock interface {
    Now() time.Time
}

// SessionService manages user sessions.
type SessionService struct {
    clock Clock
}

// NewSessionService creates a service with the provided clock.
func NewSessionService(clock Clock) *SessionService {
    return &SessionService{clock: clock}
}

// IsSessionValid checks if the session has expired.
func (s *SessionService) IsSessionValid(expiresAt time.Time) bool {
    // Compare current time against expiration.
    return s.clock.Now().Before(expiresAt)
}

Convention: Receiver names are usually one or two letters matching the type. The method uses (s *SessionService) because s matches the type. Avoid this or self. Convention: Accept interfaces, return structs. The constructor returns a *SessionService struct, but the struct holds a Clock interface. This keeps the implementation flexible and testable.

Here's the mock implementation for the test. The mock implements the Clock interface by storing a function.

package service

import "time"

// MockClock implements the Clock interface for testing.
type MockClock struct {
    NowFunc func() time.Time
}

// Now returns the time from the mock function.
func (m *MockClock) Now() time.Time {
    return m.NowFunc()
}

Here's the test using the mock. It creates a mock clock, injects it into the service, and verifies behavior.

package service

import (
    "testing"
    "time"
)

func TestIsSessionValid(t *testing.T) {
    // Create a mock clock that returns a fixed time.
    mockClock := &MockClock{
        NowFunc: func() time.Time {
            return time.Date(2024, 6, 1, 0, 0, 0, 0, time.UTC)
        },
    }

    svc := NewSessionService(mockClock)

    // Test an expired session.
    expiredTime := time.Date(2024, 5, 1, 0, 0, 0, 0, time.UTC)
    if svc.IsSessionValid(expiredTime) {
        t.Error("expected session to be invalid")
    }

    // Test a valid session.
    validTime := time.Date(2024, 7, 1, 0, 0, 0, 0, time.UTC)
    if !svc.IsSessionValid(validTime) {
        t.Error("expected session to be valid")
    }
}

The struct approach is safe for parallel tests. Each test creates its own mock and service instance. There is no shared mutable state. Go runs tests in parallel by default. Struct injection prevents race conditions between tests.

Accept interfaces, return structs. Inject the clock, not the implementation.

Pitfalls and race conditions

The variable swap pattern breaks if tests run in parallel. Go runs tests in parallel by default. If two tests modify nowFunc at the same time, you get a data race. The compiler won't catch this. The race detector will.

Run tests with the -race flag to find these bugs. The race detector reports WARNING: DATA RACE followed by a stack trace showing the conflicting read and write. If you see this, switch to struct injection or ensure tests don't run in parallel.

Timezone issues cause silent failures. time.Now() returns local time. Tests might pass on a developer's machine in UTC and fail on a CI server in a different zone. Always use time.UTC in tests. Convert to local time only at the boundary, like when rendering output for a user.

Mocking delays and ticks is harder. time.Sleep and time.After block the goroutine. Mocking time.Now doesn't help with these. You need a Timer interface that returns a channel. The mock timer can send on the channel immediately or after a controlled delay. This requires more boilerplate but keeps tests fast.

Convention: Run gofmt on your code. The formatting is decided by the tool. Most editors run it on save. Don't argue about indentation. Focus on logic.

Run the race detector. Flaky tests hide real bugs.

Decision matrix

Use a package-level variable when the function is small, the package has no other dependencies, and you run tests sequentially.

Use a struct with an interface field when the code is part of a larger service, needs to be testable in parallel, or interacts with other injected dependencies.

Use a function parameter when the time is an input to the operation, like calculating a duration from a specific start point.

Use testing.F or subtests with isolated state when you need to verify behavior across many time points without resetting globals.

Use a dedicated mock library like testify/mock only when the interface has many methods and manual mocks become repetitive.

Mock the interface, not the implementation. Keep the clock out of your business logic.

Where to go next