177
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

updated at

go test で出来ること

goで標準に用意されているtestパッケージ。
その使い方や便利な関数について

実行の仕方

go testコマンドで実行できる。この場合はカレントにあるテスト。
go test <package名> でパッケージ指定できる。

% go test strconv
ok      strconv 3.395s

テストの書き方

テストは _test.goというファイルに、TestXxxxという関数名をつける。

$GOROOT/src/net/http/serve_test.go
func TestSetsRemoteAddr(t *testing.T) {
  defer afterTest(t)
  ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
    fmt.Fprintf(w, "%s", r.RemoteAddr)
  }))
  defer ts.Close()

  res, err := Get(ts.URL)
  if err != nil {
    t.Fatalf("Get error: %v", err)
  }
  body, err := ioutil.ReadAll(res.Body)
  if err != nil {
    t.Fatalf("ReadAll error: %v", err)
  }
  ip := string(body)
  if !strings.HasPrefix(ip, "127.0.0.1:") && !strings.HasPrefix(ip, "[::1]:") {
    t.Fatalf("Expected local addr; got %q", ip)
  }
}

testing.T は、主に以下のような機能が用意されている。

func (*T) Fail

実行後にそのテストは失敗となるが、そのテストの処理は続く

  • format構造や変数を渡してメッセージを表示させたい場合
    • func (*T) Error
    • func (*T) Errorf

func (*T) FatalNow

実行後にそのテストは失敗となり、そのテストの処理は止まる

  • format構造や変数を渡してメッセージを表示させたい場合
    • func (*T) Fatal
    • func (*T) Fatalf

t.Skip()

そのテストをスキップする。

func (*T) Skipfunc (*T) SkipNow のエイリアス。

両方とも、その後の処理は実行されない。

t.Log()

Printlnなどと同じように、値を実行時に表示してくれる。
直接fmt.Println()を使う場合との違いは、行数をきちんと出してくれる。

ちなみに出力は、go testは、テキストは失敗するか
-vオプションをつけたときしか表示されない。

コマンド

一部のテストだけを実行したい場合

-run regexpで実行したいテストを指定できる

go test -run TestSetsRemoteAddr

ベンチマーク

go test -bench .とするとベンチマーク用の関数も実行出来る。

例えば適当に下のようなものがあった場合

concat.go
package concat

func Concat(ss ...string) string {
    var r string
    for _, s := range ss {
        r += s
    }
    return r
}
concat_test.go
package concat

import "testing"

func BenchmarkConcat(b *testing.B) {
    var ss []string
    for n := 0; n < 100; n++ {
        ss = append(ss, "foo")
    }

    for i := 0; i < b.N; i++ {
        Concat(ss...)
    }
}

ベンチマークの結果が出てくる

% go test -bench . -benchmem
testing: warning: no tests to run
PASS
BenchmarkConcat    20000         77496 ns/op       16016 B/op         99 allocs/op

-benchmemを指定すると、1回あたりアロケーションが何回行ったかも表示する。

ベンチマークの結果から修正をする

上の場合、文字列を+で連結しているのでアロケーションが呼ばれているのでそれを減らす。
例えば bytes.Bufferを使う場合

concat.go
func Concat(ss ...string) string {
  b := bytes.NewBufferString("")
  for _, s := range ss {
    r := strings.NewReader(s)
    r.WriteTo(b)
  }
  return b.String()
}
% go test  -bench . -benchmem
testing: warning: no tests to run
PASS
BenchmarkConcat   100000         16668 ns/op        1144 B/op          8 allocs/op

アロケーションが99から8に減り、処理速度も上がっているのがわかる。

よく使うツール

benchcmp

ベンチマークの結果を比較したものを出力できる

% go get -u golang.org/x/tools/cmd/benchcmp
% go test -bench . -benchmem > before.out
# 修正後
% go test -bench . -benchmem > after.out
% benchcmp before.out after.out
benchmark           old ns/op     new ns/op     delta
BenchmarkConcat     81059         15953         -80.32%

benchmark           old allocs     new allocs     delta
BenchmarkConcat     99             8              -91.92%

benchmark           old bytes     new bytes     delta
BenchmarkConcat     16016         1144          -92.86%

prettybench

https://github.com/cespare/prettybench
ベンチマークが複数あった場合に、整形して表示してくれる。

% go get -u github.com/cespare/prettybench 
% go test  -bench . -benchmem | prettybench
PASS
benchmark          iter      time/iter   bytes alloc         allocs
---------          ----      ---------   -----------         ------
BenchmarkConcat   20000    77.88 μs/op    16016 B/op   99 allocs/op

参考

Goでアロケーションに気をつけたコードを書く方法
Goの文字列結合のパフォーマンス

Examples

出力の結果をテストしたい場合は、Examplesを使う。

func ExampleHello() {
        fmt.Println("hello")
        // Output: hello
}

func ExampleSalutations() {
        fmt.Println("hello, and")
        fmt.Println("goodbye")
        // Output:
        // hello, and
        // goodbye
}

またgodocにExampleを表示させたい場合に、利用する。

foo.go
package foo

func Foo() string {
  return "Hello"
}

foo_test.go
package foo

import "fmt"

func ExampleFoo() {
  fmt.Println(Foo())
  // Output: Hello
}
% godoc -ex=true .
PACKAGE DOCUMENTATION

package foo
    import "."


FUNCTIONS

func Foo() string

    Example:
    fmt.Println(Foo())
    // Output: Hello

ただし、ドキュメントにきちんと表示されるようにしたい場合は
名前のつけ方をきちんと行わないと正しく認識されない。

func Example() { ... }
func ExampleF() { ... }
func ExampleT() { ... }
func ExampleT_M() { ... }

詳しくは http://golang.org/pkg/testing/#pkg-overview で。

カバレッジ

go testはカバレッジの取得も出来る

go test -cover -coverprofile cover.out # カバレッジ結果をcover.outに出力
go tool cover -func=cover.out # カバレッジ結果を出力
github.com/hiroosak/gotest/foo.go:3:    Foo     100.0%
github.com/hiroosak/gotest/foo.go:9:    String      100.0%
total:                  (statements)    100.0%
go tool cover -html=cover.out # カバレッジ結果をhtmlで出力

参考

Go 1.2で追加されたコードカバレッジ解析ツールを使う

Tips

TableDrivenTests

いろいろなパターンで、関数への入力と出力を試したい場合は
あらかじめ入力と結果をセットにしたスライスを用意して、
それをループさせてひとつずつテストを行って行数を減らす。

$GOROOT/src/strconv/atoi_test.go(一部)
type atoi64Test struct {
  in  string
  out int64
  err error
}

var atoi64tests = []atoi64Test{
  {"", 0, ErrSyntax},
  {"0", 0, nil},
  {"-0", 0, nil},
  {"1", 1, nil},
  {"-1", -1, nil},
  {"12345", 12345, nil},
  {"-12345", -12345, nil},
  {"012345", 12345, nil},
  {"-012345", -12345, nil},
  {"98765432100", 98765432100, nil},
  {"-98765432100", -98765432100, nil},
  {"9223372036854775807", 1<<63 - 1, nil},
  {"-9223372036854775807", -(1<<63 - 1), nil},
  {"9223372036854775808", 1<<63 - 1, ErrRange},
  {"-9223372036854775808", -1 << 63, nil},
  {"9223372036854775809", 1<<63 - 1, ErrRange},
  {"-9223372036854775809", -1 << 63, ErrRange},
}

// ...

func TestParseInt64(t *testing.T) {
  for i := range atoi64tests {
    test := &atoi64tests[i]
    out, err := ParseInt(test.in, 10, 64)
    if test.out != out || !reflect.DeepEqual(test.err, err) {
      t.Errorf("Atoi64(%q) = %v, %v want %v, %v",
        test.in, out, err, test.out, test.err)
    }
  }
}

外部APIを扱った処理のテスト

(あとで書く)

時刻を扱ったテスト

(あとで書く)

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
177
Help us understand the problem. What are the problem?