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