Unit-testing programs depend on I/O in Go

@yuya_takeyama

2014/11/30@GoCon 2014 Autumn

At first

このスライドについて

こんにちは

本日のお題

I/O に依存したプログラムのユニットテスト

in Ruby

StringIO を使う

def test_double
  stdin  = StringIO.new("hoge\n", "r")
  stdout = StringIO.new("", "w")

  double(stdin, stdout)

  assert_equal("hoge\nhoge\n", stdout.string)
end

Python にも同名で同じような用途のクラスが存在

in Go

bytes.Buffer を使う

bytes.Buffer

io.ReadWriter

type ReadWriter interface {
    Reader
    Writer
}

io.Reader と io.Writer

type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

os.File, net.TCPConn, net.UnixConn などはこれらを満たしている。

I/O に依存したプログラムを書いてみる

$ echo hoge | go run double.go
hoge
hoge

main()

func main() {
    Double(os.Stdin, os.Stdout)
}

Double()

func Double(stdin io.Reader, stdout io.Writer) {
    buf, _ := ioutil.ReadAll(stdin)

    stdout.Write(buf)
    stdout.Write(buf)
}

ユニットテストを書いてみる

func TestDouble(t *testing.T) {
    stdin := bytes.NewBufferString("hoge\n")
    stdout := new(bytes.Buffer)

    Double(stdin, stdout)

    if stdout.String() != "hoge\nhoge\n" {
        t.Fatal("Not matched")
    }
}

Ruby のユニットテストと比較してみる

def test_double
  stdin  = StringIO.new("hoge\n", "r")
  stdout = StringIO.new("", "w")

  double(stdin, stdout)

  assert_equal("hoge\nhoge\n", stdout.string)
end

ポイント

bytes.Buffer 応用編

exec.Cmd の標準入出力を差し替えてコマンドの E2E テスト

func TestCommand(t *testing.T) {
    cmd := exec.Command("go", "run", "double.go")
    cmd.Stdin = bytes.NewBufferString("hoge\n")
    stdout := new(bytes.Buffer)
    cmd.Stdout = stdout

    _ = cmd.Run()

    if stdout.String() != "hoge\nhoge\n" {
        t.Fatal("Not matched")
    }
}

ご静聴ありがとうございました