2019年12月25日水曜日

golang で標準出力のテストをする方法

概要

golang で fmt や log で標準出力に出したログの文字列をテストする方法を紹介します
少しポイントがあるのでそこも紹介します

環境

  • macOS 10.15.2
  • golang 1.12.9

メインコード

今回はロギングに kitlog を使っています
標準の log パッケージでも問題ないですが kitlog を使う場合は事前にインストールしておきましょう

  • go get github.com/go-kit/kit/log
  • vim $GOPATH/github.com/hawksnowlog/hoge/hoge.go
package hoge

import (
    "fmt"
    "github.com/go-kit/kit/log"
    "os"
)

type Hoge struct {
    logger log.Logger
}

func NewHoge() *Hoge {
    w := log.NewSyncWriter(os.Stdout)
    logger := log.NewLogfmtLogger(w)
    return &Hoge{logger}
}

func (h *Hoge) LoggingByKitlog() {
    name := "hawksnowlog"
    age := 99
    h.logger.Log("name", name, "age", age)
}

func (h *Hoge) LoggingByFmt() {
    fmt.Println("name=hawksnowlog age=88")
}

テストコード

  • vim $GOPATH/github.com/hawksnowlog/hoge/hoge_test.go
package hoge

import (
    "bytes"
    "io"
    stdlog "log"
    "os"
    "sync"
    "testing"
)

func captureOutput(f func()) string {
    reader, writer, err := os.Pipe()
    if err != nil {
        panic(err)
    }
    stdout := os.Stdout
    stderr := os.Stderr
    defer func() {
        os.Stdout = stdout
        os.Stderr = stderr
        stdlog.SetOutput(os.Stderr)
    }()
    os.Stdout = writer
    os.Stderr = writer
    stdlog.SetOutput(writer)
    out := make(chan string)
    wg := new(sync.WaitGroup)
    wg.Add(1)
    go func() {
        var buf bytes.Buffer
        wg.Done()
        io.Copy(&buf, reader)
        out <- buf.String()
    }()
    wg.Wait()
    f()
    writer.Close()
    return <-out
}

func TestHoge_Logging(t *testing.T) {
    // h := NewHoge() ここだとエラーになる
    output := captureOutput(func() {
        h := NewHoge()
        //h.LoggingByFmt()
        h.LoggingByKitlog()
    })
    if output != "name=hawksnowlog age=99\n" {
        t.Errorf("error: %s", output)
    }
}

実行

  • go fmt github.com/hawksnowlog/hoge && go test -v github.com/hawksnowlog/hoge

ポイント

captureOutput という関数に標準出力する処理を渡します
渡した処理内で出力されるログが文字列として返ってきます

ポイントは Hoge 構造体を作成するのも captureOutput の中で行う点です
もし captureOutput の外で h := NewHoge() すると Hoge 構造体でロギングしている情報がうまく取得できません

また比較する際に改行コードなどのエスケープ文字にも注意してください
タブ区切りでログを出力している場合にはタブ文字 (\t) が入ります

標準出力をキャプチャする関数はこれ以外にもたくさん紹介されていますがどれも仕組み的には同じです

参考サイト

0 件のコメント:

コメントを投稿