Go言語での時刻依存テスト

時刻に依存する処理のテストは、実行タイミングによって結果が変わる「不安定なテスト」になりがちです。
たとえば「毎月1日にだけ特別な処理をする」「現在時刻から◯日後に通知を送る」など、日や時刻を基準にしたロジックは多くのシステムで見られます。

これらのテストはテストしたい条件によって、

  • 日が変わるとテストが通らない
  • テスト実行時に日時の箇所だけ、テストコードを書き直さないといけない

などなど、面倒なことが起きがちです。

flextimeとは

flextime(https://github.com/Songmu/flextime)は、Goのtime.Now()をラップし、任意の時刻に固定できるテスト用ライブラリです。
これにより、テスト実行時のシステム時刻に左右されない安定したテストが実現できます。

導入方法

Goモジュールで簡単に導入できます。

go get github.com/Songmu/flextime

flextimeを使ってテストコード内で時刻固定する例

flextimeを使うと、テスト中だけtime.Now()の返す値を固定できます。

  • テスト開始時に`flextime.Fix()`で時刻を固定
  • テスト終了時に`flextime.Restore()`で元に戻す(`defer`や`t.Cleanup`の活用がおすすめ)

~~~~~

import (
  "testing"
  "time"
  "github.com/Songmu/flextime"
)

func TestSomething(t *testing.T) {
  // 固定する日付を指定
  flextime.Fix(time.Date(2025, 9, 16, 12, 0, 0, 0, time.UTC))
  // defer文: この関数が終了する際にflextime.Restore()が動いて時刻が元に戻される
  defer flextime.Restore()

  // ここから下は time.Now() も flextime.Now() も 2025-09-16 12:00:00 になる
  if time.Now().Day() != 16 {
    t.Fatal("日付が固定されていません")
  }
}

~~~~~

まとめ。使用時の注意点など

  • Go言語が使われるケースが多いIoTや、バッチ処理など、時刻が重要なシステムのテストでとても有効です。
  • `flextime.Fix()`で時刻を固定したら、必ず`flextime.Restore()`で元に戻しましょう。`defer`や`t.Cleanup`を使うと安全です。
  • flextimeはグローバルに時刻を固定するため、並列実行するテスト間で時刻が競合する可能性があります。並列テストを行う場合は、時刻固定の影響範囲に注意が必要。