Edited at

Goでログ出力/標準出力をテストする

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.Stdoutos.Stderros.Fileで上書きすることができます。マジかよ…

os.Filebytes.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.Printlnos.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)終わりだよ〜