昨年Go1.10時点でのGoのテストについてまとめた。
まとめ記事を書いた後にリリースされたGo1.11からGo1.13に含まれるテスト関連の変更をまとめる。
TL;DR
- Go1.10までのテスト関連の機能、基本的な書き方は以下にまとめてある
- Go1.11に含まれるテスト関連の変更は以下の通り
go test
実行時のgo vet
の挙動の修正-memprofile
オプションがデフォルトでアロケートしたメモリを記録するようになった
- Go1.12に含まれるテスト関連の変更は以下の通り
map
型をfmt
で出力したときの結果がソートされた状態になった-benchtime
オプションで回数を指定できるようになった
- Go1.13に含まれるテスト関連の変更は以下の通り
go getコマンドに
-t`オプションが追加されたtesting.B.N
フィールドの結果が丸まらないようになったtesting.Init
関数が追加されたtesting.B.ReportMetric
メソッドを使って時前定義のベンチマークが計算できるようになった
Go1.10までのテストの書き方・基本
Go1.10までのテスト関連の機能、基本的な書き方は以下にまとめてある。Go1.Xの仕様は安定しているし、Goのテストは標準パッケージのtesting
パッケージを使うだけなので何も変わっていない。
- Goのtestを理解する in 2018 #go
- Goのtestingを理解する in 2018 - Examples編 #go
- Goのtestingを理解する in 2018 - quickサブパッケージ編 #go
- Goのtestingを理解する in 2018 - iotestサブパッケージ編 #go
Go1.11に含まれるテスト関連の変更
- Go 1.11 Release Notes
go test
実行時のgo vet
の挙動の修正
Go1.10からgo test
実行時、テストが実行される前にgo vet
コマンドが実行される。go vet
でチェック出来ていなかったunused variable
エラーが検知できるようになった。
-memprofile
オプションがデフォルトでアロケートしたメモリを記録するようになった
ベンチマークテストのオプションの一つ、-memprofile
オプションでテストの開始時からアロケートしたメモリ量を計測できるようになった。
次のようにオプションを指定してベンチマークテストを実行する。
$ go test -bench . -memprofile mem.out
goos: darwin
goarch: amd64
pkg: github.com/budougumi0617/gopl/ch11/ex06
BenchmarkPopCountByTable10-4 3235377 313 ns/op
BenchmarkPopCountByClear10-4 34185973 32.5 ns/op
...
PASS
ok github.com/budougumi0617/gopl/ch11/ex06 26.001s
実行後、取得したmem.out
を利用すると、ベンチマーク中のメモリ利用状況がわかる。
$ go tool pprof -alloc_objects ex06.test mem.out
File: ex06.test
Type: alloc_objects
Time: Oct 30, 2019 at 8:34pm (JST)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 144, 100% of 144 total
Showing top 10 nodes out of 14
flat flat% sum% cum cum%
144 100% 100% 144 100% regexp.(*bitState).reset
0 0% 100% 144 100% main.main
0 0% 100% 144 100% regexp.(*Regexp).MatchString
...
Go1.12に含まれるテスト関連の変更
- Go 1.12 Release Notes
map
型をfmt
で出力したときの結果がソートされた状態になった
Goのデータ構造はその定義に忠実だ。map
はいわゆる集合なので、順序は保証されない。
そのため、Go1.11までmap
の中身をrange
で取り出すと毎回違う順で中身が取り出されたり、fmt
パッケージで出力すると内部データが順不同で出力されていた。
Go1.12からはfmt
パッケージで出力する場合は常にソートされた状態で出力されるようになった。
これによって、Go1.11までで書くのが難しかったmap
の出力結果を比較するExamples Testが書きやすくなった。
package main
import (
"fmt"
)
func ExampleSalutations() {
m := map[string]string{
"hoge": "value1",
"foo": "value2",
}
fmt.Println(m)
// Output:
// map[foo:value2 hoge:value1]
}
「そもそもExamplesとは?」という方は次の記事を参考にしていただきたい。
-benchtime
オプションで回数を指定できるようになった
Goのテストはgo test -bench .
のように書くとベンチマークテストを実行することができる。
ここで-benchtime Xs
オプションで秒数Xs
を渡すと、通常1秒間で行なうベンチマークテストを任意のX
秒間に変更することができる。Go1.12からは-benchtime 10x
というように数字x
を引数に渡すことで秒数指定ではなく、回数指定でベンチマークテストを実行できるようになった。
$ go test -bench BenchmarkPopCountByTable10 -benchtime=2s
goos: darwin
goarch: amd64
pkg: github.com/budougumi0617/gopl/ch11/ex06
BenchmarkPopCountByTable10-4 7659824 305 ns/op
BenchmarkPopCountByTable100-4 3503498 680 ns/op
BenchmarkPopCountByTable1000-4 550845 4344 ns/op
BenchmarkPopCountByTable10000-4 58653 39997 ns/op
PASS
ok github.com/budougumi0617/gopl/ch11/ex06 12.994s
$ go test -bench BenchmarkPopCountByTable10 -benchtime=10x
goos: darwin
goarch: amd64
pkg: github.com/budougumi0617/gopl/ch11/ex06
BenchmarkPopCountByTable10-4 10 729 ns/op
BenchmarkPopCountByTable100-4 10 1276 ns/op
BenchmarkPopCountByTable1000-4 10 7114 ns/op
BenchmarkPopCountByTable10000-4 10 60297 ns/op
PASS
ok github.com/budougumi0617/gopl/ch11/ex06 0.015s
Go1.13に含まれるテスト関連の変更
- Go 1.13 Release Notes
go getコマンドに
-t`オプションが追加された
go get -u
コマンドを実行しても、通常テストに関連する依存パッケージの更新は行われない。
Go1.13からはgo get
コマンドに-t
オプションが追加され、テストで依存しているパッケージも更新できるようになった。
$ go help get | grep "\-t" -A 1
usage: go get [-d] [-f] [-t] [-u] [-v] [-fix] [-insecure] [build flags] [packages]
--
The -t flag instructs get to also download the packages required to build
the tests for the specified packages.
testing.B.N
フィールドの結果が丸まらないようになった
Go1.12以前のベンチマークテストでは、ベンチマークテスト実行時の試行回数が丸められていた。 Go1.13からベンチマークテスト実行時の試行回数として厳密な回数が表示されるようになった。
testing.Init
関数が追加された
Go1.13からテスト実行時のフラグの処理順序が変更された。
そのため、テストコードの中でフラグのパースをしている場合は、フラグのパースの前に明示的にtesting.Init
関数を呼ばないとうまく実行できないようになった。
詳細は@ktrさんのブログを参照のこと。
package main
import (
"flag"
"testing"
)
var (
myflag = flag.Bool("myflag", false, "custom flag")
)
func init() {
// testing.Init() // コメントアウトを外さないとエラー
flag.Parse()
}
func TestHello(t *testing.T) {
}
/*
* 実行結果
* flag provided but not defined: -test.v
* Usage of NaClMain:
* -myflag
* custom flag
*/
testing.B.ReportMetric
メソッドを使って時前定義のベンチマークが計算できるようになった
Goの標準のベンチマークテストを実行すると、既定の計測基準でベンチマークスコアが測定される。
$ go test -bench . -benchmem
goos: darwin
goarch: amd64
pkg: github.com/budougumi0617/gopl/ch11/ex06
BenchmarkPopCountByTable10-4 3295702 311 ns/op 0 B/op 0 allocs/op
BenchmarkPopCountByClear10-4 36390008 32.5 ns/op 0 B/op 0 allocs/op
...
Go1.13からはこれらに加えて自分で定義したカスタムスコアを設定することでできるようになった。
func BenchmarkSort(b *testing.B) {
var cmps int64
// ベンチマークテストを書く
// 独自のベンチマーク基準を設定しておく
b.ReportMetric(float64(cmps)/float64(b.N), "cmps/op")
}
例えば、外部リソースを模したモックなどにカウンタを設置し、何回呼び出されているかを計測するなど、色々考えられる。 詳細は@ymotongpooさんの資料などを参照のこと。
- testing.B.ReportMetric @Go1.13 Release Party
- testing.B.ReportMetric | commaok.xyz
終わりに
さくっと1時間くらいでまとめられるかなと思って始めたが、英語がちゃんと読めなかったりベンチマークらへんをあまり理解できていないせいでだいぶ時間がかかった。
私自身あまりベンチマークやプロファイリングなどを取ったことないので、改めて今度基礎からpprof
などをいじってみよう。
参考
- Goのtestを理解する in 2018 #go
- Goのtestingを理解する in 2018 - Examples編 #go
- Go 1.11 Release Notes
- Go 1.12 Release Notes
- Go 1.13 Release Notes
- Goのプロファイラを使ってメモリ割り当て回数を減らす
- Go 1.13 にアップデートするとテスト時に “flag provided but not defined” エラーが発生するケース
- testing.B.ReportMetric | Go1.13 Release Party
- testing.B.ReportMetric | commaok.xyz