Goのlogパッケージや標準出力内容の結果をテストをしたいときのメモ。
ログ出力のテスト
Goのlogパッケージのlog.PrintXXのログ出力先はデフォルトではos.Stderrですが、log.SetOutputによって出力先を変更することができます。
log.SetOutput
func SetOutput(w io.Writer)
テストする関数は以下の通りです。
trysail.go
type member int
const (
	mocho member = iota
	sora
	nansu
)
func printLog(m member) {
	switch m {
	case mocho:
		log.Print("(o・∇・o)終わりだよ〜")
	case sora:
		log.Print("(°Q`)")
	case nansu:
		log.Print("(*>△<)<ナーンナーンっっ")
	}
}
引数によって出力するログの内容が変わる単純な関数です。
ではテストコードを書いてみましょう
trysail_test.go
func Test_printLog(t *testing.T) {
	// ログの出力先をバッファに変更
	var buf bytes.Buffer
	log.SetOutput(&buf)
	// デフォルトだと日付が出力されてしまうので、フラグに0を設定する
	defaultFlags := log.Flags()
	log.SetFlags(0)
	// Test_printLogテスト終了時、変更した内容を戻す
	defer func() {
		log.SetOutput(os.Stderr)
		log.SetFlags(defaultFlags)
	}()
	t.Run("Mocho", func(t *testing.T) {
		// 次のテスト(Nansu)に影響がないように、Mochoテスト終了時にバッファの内容をクリアする
		defer func() {
			buf.Reset()
		}()
		printLog(mocho)
		expected := "(o・∇・o)終わりだよ〜"
		// 末尾に改行コードが入っているので除去する
		actual := strings.TrimRight(buf.String(), "\n")
		if actual != expected {
			t.Fatalf("failed! actual = %s, expected = %s", actual, expected)
		}
	})
	t.Run("Nansu", func(t *testing.T) {
		defer func() {
			buf.Reset()
		}()
		printLog(nansu)
		expected := "(*>△<)<ナーンナーンっっ"
		actual := strings.TrimRight(buf.String(), "\n")
		if actual != expected {
			t.Fatalf("failed! actual = %s, expected = %s", actual, expected)
		}
	})
}
標準出力のテスト
なんと、Go言語の標準出力や標準入力であるos.Stdoutやos.Stderrはos.Fileで上書きすることができます。マジかよ…
os.Fileをbytes.Bufferにつなぐためにos.Pipeを利用します。
テストする関数は以下の通りです。
trysail.go
type member int
const (
	mocho member = iota
	sora
	nansu
)
func print(m member) {
	switch m {
	case mocho:
		fmt.Println("(o・∇・o)終わりだよ〜")
	case sora:
		fmt.Println("(°Q`)")
	case nansu:
		fmt.Println("(*>△<)<ナーンナーンっっ")
	}
}
先ほどの関数のfmt.Println版です。
fmt.Printlnはos.Stdoutに書き込むので、そちらをbytes.BufferにPipeします。
func Test_print(t *testing.T) {
	tmpStdout := os.Stdout
	defer func() {
		os.Stdout = tmpStdout
	}()
	t.Run("Mocho", func(t *testing.T) {
		r, w, _ := os.Pipe()
		os.Stdout = w
		print(mocho)
		w.Close() // クローズしないと永遠に読み込み待ち状態になるよ
		var buf bytes.Buffer
		buf.ReadFrom(r)
		expected := "(o・∇・o)終わりだよ〜"
		actual := strings.TrimRight(buf.String(), "\n")
		if actual != expected {
			t.Fatalf("failed! actual = %s, expected = %s", actual, expected)
		}
	})
	t.Run("Nansu", func(t *testing.T) {
		r, w, _ := os.Pipe()
		os.Stdout = w
		print(nansu)
		w.Close()
		var buf bytes.Buffer
		buf.ReadFrom(r)
		expected := "(*>△<)<ナーンナーンっっ"
		actual := strings.TrimRight(buf.String(), "\n")
		if actual != expected {
			t.Fatalf("failed! actual = %s, expected = %s", actual, expected)
		}
	})
}
意外と簡単にできた。
(o・∇・o)終わりだよ〜