Skip to content

feat: custom mock logger #21

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 35 additions & 8 deletions mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
// during a test, triggering any timers or tickers automatically.
type Mock struct {
tb testing.TB
logger Logger
mu sync.Mutex
testOver bool

Expand Down Expand Up @@ -199,7 +200,7 @@ func (m *Mock) matchCallLocked(c *apiCall) {
}
}
if !m.testOver {
m.tb.Logf("Mock Clock - %s call, matched %d traps", c, len(traps))
m.logger.Logf("Mock Clock - %s call, matched %d traps", c, len(traps))
}
if len(traps) == 0 {
return
Expand Down Expand Up @@ -265,7 +266,7 @@ func (m *Mock) Advance(d time.Duration) AdvanceWaiter {
w := AdvanceWaiter{tb: m.tb, ch: make(chan struct{})}
m.mu.Lock()
if !m.testOver {
m.tb.Logf("Mock Clock - Advance(%s)", d)
m.logger.Logf("Mock Clock - Advance(%s)", d)
}
fin := m.cur.Add(d)
// nextTime.IsZero implies no events scheduled.
Expand Down Expand Up @@ -315,7 +316,7 @@ func (m *Mock) Set(t time.Time) AdvanceWaiter {
w := AdvanceWaiter{tb: m.tb, ch: make(chan struct{})}
m.mu.Lock()
if !m.testOver {
m.tb.Logf("Mock Clock - Set(%s)", t)
m.logger.Logf("Mock Clock - Set(%s)", t)
}
if t.Before(m.cur) {
defer close(w.ch)
Expand Down Expand Up @@ -354,7 +355,7 @@ func (m *Mock) Set(t time.Time) AdvanceWaiter {
func (m *Mock) AdvanceNext() (time.Duration, AdvanceWaiter) {
m.mu.Lock()
if !m.testOver {
m.tb.Logf("Mock Clock - AdvanceNext()")
m.logger.Logf("Mock Clock - AdvanceNext()")
}
m.tb.Helper()
w := AdvanceWaiter{tb: m.tb, ch: make(chan struct{})}
Expand Down Expand Up @@ -445,7 +446,7 @@ func (m *Mock) newTrap(fn clockFunction, tags []string) *Trap {
m.mu.Lock()
defer m.mu.Unlock()
if !m.testOver {
m.tb.Logf("Mock Clock - Trap %s(..., %v)", fn, tags)
m.logger.Logf("Mock Clock - Trap %s(..., %v)", fn, tags)
}
tr := &Trap{
fn: fn,
Expand All @@ -458,6 +459,18 @@ func (m *Mock) newTrap(fn clockFunction, tags []string) *Trap {
return tr
}

// WithLogger replaces the default testing logger with a custom one.
//
// This can be used to discard log messages with:
//
// quartz.NewMock(t).WithLogger(quartz.NoOpLogger)
func (m *Mock) WithLogger(l Logger) *Mock {
m.mu.Lock()
defer m.mu.Unlock()
m.logger = l
return m
}

// NewMock creates a new Mock with the time set to midnight UTC on Jan 1, 2024.
// You may re-set the time earlier than this, but only before timers or tickers
// are created.
Expand All @@ -467,14 +480,15 @@ func NewMock(tb testing.TB) *Mock {
panic(err)
}
m := &Mock{
tb: tb,
cur: cur,
tb: tb,
logger: tb,
cur: cur,
}
tb.Cleanup(func() {
m.mu.Lock()
defer m.mu.Unlock()
m.testOver = true
tb.Logf("Mock Clock - test cleanup; will no longer log clock events")
m.logger.Logf("Mock Clock - test cleanup; will no longer log clock events")
})
return m
}
Expand Down Expand Up @@ -806,3 +820,16 @@ func (t *Trap) MustWait(ctx context.Context) *Call {
}
return c
}

type Logger interface {
Log(args ...any)
Logf(format string, args ...any)
}

// NoOpLogger is a Logger that discards all log messages.
var NoOpLogger Logger = noOpLogger{}

type noOpLogger struct{}

func (noOpLogger) Log(args ...any) {}
func (noOpLogger) Logf(format string, args ...any) {}
46 changes: 46 additions & 0 deletions mock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package quartz_test
import (
"context"
"errors"
"fmt"
"testing"
"time"

Expand Down Expand Up @@ -407,6 +408,39 @@ func Test_UnreleasedCalls(t *testing.T) {
})
}

func Test_WithLogger(t *testing.T) {
t.Parallel()

tl := &testLogger{}
mClock := quartz.NewMock(t).WithLogger(tl)
mClock.Now("test", "Test_WithLogger")
if len(tl.calls) != 1 {
t.Fatalf("expected 1 call, got %d", len(tl.calls))
}
expectLogLine := "Mock Clock - Now([test Test_WithLogger]) call, matched 0 traps"
if tl.calls[0] != expectLogLine {
t.Fatalf("expected log line %q, got %q", expectLogLine, tl.calls[0])
}

mClock.NewTimer(time.Second, "timer")
if len(tl.calls) != 2 {
t.Fatalf("expected 2 calls, got %d", len(tl.calls))
}
expectLogLine = "Mock Clock - NewTimer(1s, [timer]) call, matched 0 traps"
if tl.calls[1] != expectLogLine {
t.Fatalf("expected log line %q, got %q", expectLogLine, tl.calls[1])
}

mClock.Advance(500 * time.Millisecond)
if len(tl.calls) != 3 {
t.Fatalf("expected 3 calls, got %d", len(tl.calls))
}
expectLogLine = "Mock Clock - Advance(500ms)"
if tl.calls[2] != expectLogLine {
t.Fatalf("expected log line %q, got %q", expectLogLine, tl.calls[2])
}
}

type captureFailTB struct {
failed bool
testing.TB
Expand Down Expand Up @@ -455,3 +489,15 @@ func tRunFail(t testing.TB, f func(t testing.TB)) {
t.Fatal("want test to fail")
}
}

type testLogger struct {
calls []string
}

func (l *testLogger) Log(args ...any) {
l.calls = append(l.calls, fmt.Sprint(args...))
}

func (l *testLogger) Logf(format string, args ...any) {
l.calls = append(l.calls, fmt.Sprintf(format, args...))
}