概要
Bazel解説第6弾です。
Bazelを使ってみる その1(Goのビルド) - Carpe Diem
Bazelを使ってみる その2(protobufのビルド) - Carpe Diem
Bazelを使ってみる その3(docker imageのビルド) - Carpe Diem
Bazelを使ってみる その4(gRPCのビルド) - Carpe Diem
Bazelを使ってみる その5(リモートキャッシュ) - Carpe Diem
今回はテストについて説明します。
環境
- Bazel v4.2.2
各種テスト
Goにおけるテスト周りでは以下の考慮できてれば大丈夫でしょう。
- unit test
- raceフラグを付けたい場合
- coverage
- integration test
今回は以下のようなファイル構成の際において
├── dice │ ├── dice.go │ ├── dice_integration_test.go │ └── dice_test.go ├── docker-compose.yml ├── go.mod └── go.sum
それぞれのやり方を説明します。
unit test
BUILD.bazelの設定
Gazelleを使っている場合
$ bazel run //:gazelle
を実行すると、_test.go
ファイルが有ればそれをgo_test
ルールに含んでテスト対象としてくれます。
今回だと以下のdice/BUILD.bazel
が生成されます。
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "dice", srcs = ["dice.go"], importpath = "github.com/jun06t/bazel-sample/test/dice", visibility = ["//visibility:public"], deps = ["@com_github_go_redis_redis_v8//:go_default_library"], ) go_test( name = "dice_test", srcs = ["dice_test.go"], embed = [":dice"], deps = ["@com_github_stretchr_testify//assert:go_default_library"], )
実行方法
$ bazel test //...
とすれば対象のテストをすべて実行してくれます。
raceフラグを付けたい場合
raceフラグを付けたい時は、先程のコマンドに@io_bazel_rules_go//go/config:race
オプションを付けます。
$ bazel test --@io_bazel_rules_go//go/config:race //...
デフォルトでONにしたい場合は.bazelrc
で以下を追記すると良いでしょう。
test --@io_bazel_rules_go//go/config:race
coverage
カバレッジを出力したい場合はbazelでコマンドが用意されています。
$ bazel coverage //...
これでcoverage.dat
という名前のカバレッジレポートを生成できます。
ただ出力されたレポートはサンドボックスにある&パッケージ毎にバラバラになっています。
なので以下のようにコマンドでまとめる処理を入れると良いでしょう。
echo "mode: set" > coverage.out find bazel-out/ -name "coverage.dat" | xargs tail -q -n +2 >> coverage.out
レポートの確認
$ go tool cover -html=coverage.out
問題なく表示できています。
ビルドタグを用いたintegration test
BUILD.bazelの対応
Goでintegration testを行う際、ビルドタグを用いてunit testとは分ける手法があります。
通常ビルドタグが付いたテストはgazelleでビルドしてもgo_test
の対象からは含まれなくなります。
対象にしたい場合は
$ bazel run //:gazelle -- -build_tags=integration
とすると、先程のdice/BUILD.bazel
にintegration testも含めるようにしてくれます。
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "dice", srcs = ["dice.go"], importpath = "github.com/jun06t/bazel-sample/test/dice", visibility = ["//visibility:public"], deps = ["@com_github_go_redis_redis_v8//:redis"], ) go_test( name = "dice_test", srcs = [ "dice_integration_test.go", "dice_test.go", ], embed = [":dice"], deps = [ "@com_github_stretchr_testify//assert", "@com_github_stretchr_testify//require", ], )
実行方法
go test -tags integration
のようにタグ付きで実行したい時はio_bazel_rules_go//go/config:tags
オプションを付けます。
$ bazel test --@io_bazel_rules_go//go/config:tags=integration //...
このようなビルドオプションは rules_go/modes.rst at master · bazelbuild/rules_go · GitHub にまとまっています。
ローカルのファイルを用いたテスト
fixturesのようなテスト用のデータファイルを使いたい場合です。
同じディレクトリ
以下のディレクトリ構造でdice_test.go
がtest1.yaml
に依存しているとします。
. ├── BUILD.bazel ├── WORKSPACE ├── deps.bzl ├── dice │ ├── BUILD.bazel # ここを編集 │ ├── dice.go │ ├── dice_integration_test.go │ ├── dice_test.go │ └── test1.yaml # 対象のデータファイル ├── go.mod └── go.sum
この場合は以下のようにdata
属性を使って依存関係を解決できます。
go_test( name = "dice_test", srcs = [ "dice_integration_test.go", "dice_test.go", ], data = [ "test1.yaml", ], embed = [":dice"], deps = [ "@com_github_stretchr_testify//assert", "@com_github_stretchr_testify//require", ], )
サブディレクトリ
サブディレクトリにファイルが存在する場合はglobでディレクトリ全体を指定すると良いです。
. ├── BUILD.bazel ├── WORKSPACE ├── deps.bzl ├── dice │ ├── BUILD.bazel # ここを編集 │ ├── dice.go │ ├── dice_integration_test.go │ ├── dice_test.go │ └── testdata │ └── test2.yaml # 対象のデータファイル ├── go.mod └── go.sum
go_test( name = "dice_test", srcs = [ "dice_integration_test.go", "dice_test.go", ], data = glob(["testdata/**"]), embed = [":dice"], deps = [ "@com_github_stretchr_testify//assert", "@com_github_stretchr_testify//require", ], )
複数箇所に存在する場合
以下のように複数の場所に存在する場合はfilegroup()
を使ってターゲットを定義して指定するとできます。
. ├── BUILD.bazel ├── WORKSPACE ├── deps.bzl ├── dice │ ├── BUILD.bazel # ここを編集 │ ├── dice.go │ ├── dice_integration_test.go │ ├── dice_test.go │ ├── test1.yaml # 対象のデータファイル │ └── testdata │ └── test2.yaml # 対象のデータファイル ├── go.mod └── go.sum
go_test( name = "dice_test", srcs = [ "dice_integration_test.go", "dice_test.go", ], data = [ "test1.yaml", ":test_dir", ], embed = [":dice"], deps = [ "@com_github_stretchr_testify//assert", "@com_github_stretchr_testify//require", ], ) filegroup( name = "test_dir", srcs = glob(["testdata/**"]), )
その他
テストで役立つオプション
あらかじめ.bazelrc
に以下を追記しておくと、エラーの表示などが見やすくなります。
build --show_timestamps --verbose_failures test --test_output=errors --test_verbose_timeout_warnings
各オプションを説明すると以下です。
オプション | 説明 |
---|---|
show_timestamps | 各アクションのタイムスタンプをコンソールで表示する |
verbose_failures | 失敗した際にどんなコマンドだったか表示する |
test_output | summary , errors , all , streamed のどれかを設定する。デフォルトはstreamed errors にすると失敗時にログを表示してくれる |
test_verbose_timeout_warnings | 実際のテストの実行時間がテストによって定義されたタイムアウトと一致しない場合に追加の警告を表示する |
runフラグはなさそう
Goに対応したビルドオプションであったり、Bazel, GazelleのIssueをざっと見てみましたが、-run
にあたるオプションは見当たりませんでした。
Bazelを使う際の方針にも関係しますが「無理にすべてをBazelに任せる必要はなく、CIなど再現性が重要視されるされるケースで使えば良い」というくらいの緩い方針で使うのがちょうど良いでしょう。
なので-run
など一部だけテストしたい時は通常通りのgo test -run=xxx
で良いと思います。
サンプルコード
今回のコードはこちら。
まとめ
Bazelを使った場合のGoでのテスト周りについてまとめました。