🧪

Goで標準入力を受け取る関数をテストしたい

2021/10/29に公開約1,300字

あらすじ

  • Goのテスト書きまくるぜひゃっはーとしていたら、標準入力を使う関数でエラーが…。
  • どうしたらええねん…。
  • 答えを求めてネットの海を漂うと、os.Stdinを置き換えるのじゃ、という知見が
  • いやいやそんなそんな、と思ってGo本体のソースコードを見ると、Stdinのテストコードでもまさしくそんなことをしてるじゃないですか
  • ということでそれに則ってトライしてみます

使うもの

  • Go 1.17
  • osパッケージ

調べもの

  • os.Stdinの返り値は*os.File。それと同じ戻り値を与える関数なら置き換えられる
  • 意外と選択肢が少ない
  • 妥当そうなところだとos.CreateTempがありそう。※Go1.16以前はioutil.TempFile
  • 標準入力したい文字列を書き込んで、ファイルの先頭にseekして、使い終わったら消すという流れになりそう
  • 結果的にネットで見かけたやり方とほぼ等価
  • もろもろをdeferし忘れる自信しかないので、次のように実装して少しは忘れづらいようにしました。
// StdinAlt returns Alt(*os.File), closer(func), error
// Alt: intended to replace os.Stdin while test
// closer: Close and remove files internally generated by ALt
func StdiinAlt(s string) (*os.File, func(*os.File) error, error) {
	f, err := os.CreateTemp("", "tmp")
	if err != nil {
		return nil, nil, err
	}
	if _, err := f.Write([]byte(s)); err != nil {
		return nil, nil, err
	}
	// jump to head
	f.Seek(0, 0)

	closer := func(file *os.File) error {
		if err := file.Close(); err != nil {
			return err
		}
		os.Remove(file.Name())
		return nil
	}
	return f, closer, nil
}

func TestStdinAlt(t *testing.T) {
	alt, closer, _ := StdiinAlt("test")
	defer closer(alt)

	old := os.Stdin
	defer func() { os.Stdin = old }()

	os.Stdin = alt
	sc := bufio.NewScanner(os.Stdin)
	sc.Scan()
	s := sc.Text()
	if s != "test" {
		t.Errorf("got %s", s)
	}
}
GitHubで編集を提案

Discussion

ログインするとコメントできます