Golangで標準入力を使用する関数のテスト
概要
Golangの関数内で標準入力の値を受け取る関数に単体テストを行う方法をまとめました。
fmt.Scanで標準入力の値を受け取る関数
fmt.Scanを使用して標準入力から値を受け取る関数は、そのままだと単体テストができません。
下記のinputStdin関数は、関数内で標準入力の内容を受け取り
標準入力の内容をそのまま返す関数です。
1func inputStdin() string {
2 var inputValue string
3 fmt.Println("入力してください")
4 fmt.Print(">")
5 fmt.Scan(&inputValue)
6 return inputValue
7}
inputStdinに単体テストを行うとエラーになります。
1func TestInputStdin(t *testing.T) {
2 got := inputStdin()
3 want := "test"
4 if got != want {
5 t.Errorf("got %s, want %s", got, want)
6 }
7}
1go test
2入力してください
3>--- FAIL: TestInputStdin (0.00s)
4 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
1func Scan(a ...any) (n int, err error) {
2 return Fscan(os.Stdin, a...)
3}
Fscanのコードを確認してみると、第一引数にio.Readerインターフェースの要件を満たす型を渡すことができます。
引数rのio.Readerにos.Stdin以外の引数を渡すとテストできそうです。
1func Fscan(r io.Reader, a ...any) (n int, err error) {
2 s, old := newScanState(r, true, false)
3 n, err = s.doScan(a)
4 s.free(old)
5 return
6}
fmt.Fscanに書き換えて単体テスト
fmt.Scanだとos.Stdinから値を読み込んでしまうため、代わりにfmt.Fscanを使用し
任意のio.Readerから値を取得できるようにします。
1func inputStdin2(in io.Reader) string {
2
3 if in == nil {
4 in = os.Stdin
5 }
6
7 var inputValue string
8 fmt.Println("入力してください")
9 fmt.Print(">")
10 fmt.Fscan(in, &inputValue)
11 return inputValue
12}
1func inputStdin2(in io.Reader) string {
引数にio.Readerインターフェースの要件を満たすものを渡します。
1 if in == nil {
2 in = os.Stdin
3 }
引数のinがnilのときはos.Stdinを入力に使用します。
1fmt.Fscan(in, &inputValue)
fmt.Scanの代わりにfmt.Fscanを使用し、第一引数にinを渡します。
第二引数に値を書き込みたい変数を渡します。
inputStdin2に単体テストを行ってみます。
1func TestInputStdin2(t *testing.T) {
2 in := bytes.NewBufferString("test")
3 got := inputStdin2(in)
4 want := "test"
5 if got != want {
6 t.Errorf("got %s, want %s", got, want)
7 }
8}
1in := 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と表示されます。
1go test
2入力してください
3>PASS
4ok 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