Golangで標準入力を使用する関数のテスト

Golangの関数内で標準入力の値を受け取る関数に単体テストを行う方法をまとめました。

fmt.Scanで標準入力の値を受け取る関数

fmt.Scanを使用して標準入力から値を受け取る関数は、そのままだと単体テストができません。 下記のinputStdin関数は、関数内で標準入力の内容を受け取り、標準入力の内容をそのまま返す関数です。

func inputStdin() string {
	var inputValue string
	fmt.Println("入力してください")
	fmt.Print(">")
	fmt.Scan(&inputValue)
	return inputValue
}

inputStdinに単体テストを行うとエラーになります。

func TestInputStdin(t *testing.T) {
	got := inputStdin()
	want := "test"
	if got != want {
		t.Errorf("got %s, want %s", got, want)
	}
}
# テストを実行
>go test
入力してください
>--- FAIL: TestInputStdin (0.00s)
    main_test.go:12: got , want test

fmt.Scanの確認

fmt.Scanのコードを確認してみると、os.Stdinを第一引数に渡してFscanを呼び出しています。

https://cs.opensource.google/go/go/+/refs/tags/go1.18.5:src/fmt/scan.go;l=63

func Scan(a ...any) (n int, err error) {
	return Fscan(os.Stdin, a...)
}

Fscanのコードを確認してみると、第一引数にio.Readerインターフェースの要件を満たす型を渡すことができます。引数rのio.Readerにos.Stdin以外の引数を渡すとテストできそうです。

func Fscan(r io.Reader, a ...any) (n int, err error) {
	s, old := newScanState(r, true, false)
	n, err = s.doScan(a)
	s.free(old)
	return
}

fmt.Fscanに書き換えて単体テスト

fmt.Scanだとos.Stdinから値を読み込んでしまうため、代わりにfmt.Fscanを使用し、 任意のio.Readerから値を取得できるようにします。

func inputStdin2(in io.Reader) string {

	if in == nil {
		in = os.Stdin
	}

	var inputValue string
	fmt.Println("入力してください")
	fmt.Print(">")
	fmt.Fscan(in, &inputValue)
	return inputValue
}
func inputStdin2(in io.Reader) string {

引数にio.Readerインターフェースの要件を満たすものを渡します。

	if in == nil {
		in = os.Stdin
	}

引数のinがnilのときはos.Stdinを入力に使用します。

fmt.Fscan(in, &inputValue)

fmt.Scanの代わりにfmt.Fscanを使用し、第一引数にinを渡します。
第二引数に値を書き込みたい変数を渡します。

inputStdin2に単体テストを行ってみます。

func TestInputStdin2(t *testing.T) {
	in := bytes.NewBufferString("test")
	got := inputStdin2(in)
	want := "test"
	if got != want {
		t.Errorf("got %s, want %s", got, want)
	}
}
in := bytes.NewBufferString("test")

bytes.NewBufferStringを利用してBuffer構造体を作成します。 Buffer構造体はio.Readerのインターフェース要件を満たしているため、inputStdin2の引数に渡すことができます。

https://cs.opensource.google/go/go/+/refs/tags/go1.18.5:src/bytes/buffer.go;l=297

テストを行うとokと表示されます。

go test
入力してください
>PASS
ok      stdin-unittest  0.223s

参考URL

https://zenn.dev/hsaki/books/golang-io-package/viewer/fmt

https://stackoverflow.com/questions/17456901/how-to-write-tests-against-user-input-in-go

Licensed under CC BY-NC-SA 4.0
最終更新 2024年1月30日 13:39
Hugo で構築されています。
テーマ StackJimmy によって設計されています。