Go言語:システム日付を固定してtestingパッケージでテストを行う方法を解説

Go

Go言語では、時間の操作に対して組み込みの機能が用意されていますが、システムの現在時刻を任意の日付に変更する機能は提供されていません。しかし、テストのコンテクスト内で時間を操作するためのパターンはあります。具体的には、システム時間を返す関数をラップし、それをテストで任意の値に置き換えるというパターンです。

testing package - testing - Go Packages
Package testing provides support for automated testing of Go packages.
testing package - testing - Go Packages
Package testing provides support for automated testing of Go packages.

Go言語のテスト技術を磨く:testingパッケージの応用的な使い方を完全解説

Go言語のテスト技術を磨く:testingパッケージの応用的な使い方を完全解説
はじめに この記事では、Go言語のtestingパッケージを用いたテスト作成における応用的な技術について解説します。具体的には、テストのグルーピング、モック作成、テスト前後に呼ばれる関数の設定、エラーチェック、テストのスキップ、システム日付...

以下に、このパターンを使用したコードスニペットを示します。

package main

import (
	"fmt"
	"testing"
	"time"
)

// 現在時間を返す関数をラップする
var nowFunc = func() time.Time {
	return time.Now()
}

// 現在時間を返す関数
func Now() time.Time {
	return nowFunc()
}

func TestNow(t *testing.T) {
	// テスト中はnowFuncを任意の値に置き換える
	nowFunc = func() time.Time {
		return time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
	}
	defer func() { nowFunc = time.Now }() // テスト終了後に元の関数に戻す

	fmt.Println(Now()) // 2022-01-01 00:00:00 +0000 UTC
}

このコードでは、Now関数はnowFuncという変数を通じて現在の日時を返します。テストでは、nowFuncを任意の日時を返す関数に置き換えます。テスト終了後は、nowFuncを元のtime.Now関数に戻します。これにより、テスト中だけ時間を任意の値に固定することができます。

しかし、このパターンには注意点があります。複数のテストが同時に実行される場合(例えば、並列テスト)、それらのテスト間でnowFuncが共有されるため、テストが他のテストの結果に影響を与える可能性があります。したがって、このパターンはテストが並列に実行されない場合、またはそれぞれのテストが独自のnowFuncを持つ場合にのみ適しています。