gotestsumを使いこなしてGoのテスト体験を向上したい
はじめに
突然ですがGoのテスト結果って見づらくないでしょうか?
Goのテスト実行例
% go test -v ./...
? gotestsum-demo/fizzbuzz [no test files]
=== RUN TestAdd
=== PAUSE TestAdd
=== CONT TestAdd
=== RUN TestAdd/1+0
=== PAUSE TestAdd/1+0
=== RUN TestAdd/10+10
=== PAUSE TestAdd/10+10
=== RUN TestAdd/1+1
=== PAUSE TestAdd/1+1
=== RUN TestAdd/2+1
=== PAUSE TestAdd/2+1
=== RUN TestAdd/1-1
=== PAUSE TestAdd/1-1
=== RUN TestAdd/1+2
=== PAUSE TestAdd/1+2
=== CONT TestAdd/1+0
=== CONT TestAdd/1-1
=== CONT TestAdd/2+1
=== CONT TestAdd/10+10
=== CONT TestAdd/1+2
=== CONT TestAdd/1+1
--- PASS: TestAdd (0.00s)
--- PASS: TestAdd/1+0 (0.00s)
--- PASS: TestAdd/2+1 (0.00s)
--- PASS: TestAdd/10+10 (0.00s)
--- PASS: TestAdd/1+2 (0.00s)
--- PASS: TestAdd/1-1 (0.00s)
--- PASS: TestAdd/1+1 (0.00s)
PASS
ok gotestsum-demo/calculate 0.400s
=== RUN TestHello
=== PAUSE TestHello
=== RUN TestHello2
--- PASS: TestHello2 (0.00s)
=== CONT TestHello
=== RUN TestHello/Hello,_gotestsum!!
=== PAUSE TestHello/Hello,_gotestsum!!
=== RUN TestHello/Hello,_World!!
=== PAUSE TestHello/Hello,_World!!
=== RUN TestHello/Hello,_Go!!
=== PAUSE TestHello/Hello,_Go!!
=== CONT TestHello/Hello,_gotestsum!!
=== CONT TestHello/Hello,_Go!!
=== CONT TestHello/Hello,_World!!
--- PASS: TestHello (0.00s)
--- PASS: TestHello/Hello,_gotestsum!! (0.00s)
--- PASS: TestHello/Hello,_Go!! (0.00s)
--- PASS: TestHello/Hello,_World!! (0.00s)
PASS
ok gotestsum-demo/hello 0.206s
-v
オプションをつけたりすると出力がやたら多くなるうえに全て白文字なので成功したのか失敗したのかがパッと見でわかりづらい気がします。
Goはリッチな開発者体験を提供するよりもパフォーマンスや安全性などを重視する文化のようなものがあると思っているのであんまり気にしたことなかったのですが最近他の言語でテストを書いたりするとテスト結果が見やすいなーと思うことがありました。
さすがに色付きでテスト結果を表示したいなと思い調べたところgotestsumというツールが便利そうだったのでいろいろ触った備忘録です。
(正直VSCodeのようなエディタからテストを実行していたりするとエディタ側で見やすく表示してくれたりするのであんまりターミナルからテストを実行することも多くないんですが...)
成果物はこちら
この記事で紹介すること
- gotestsumを使ったローカルでのテスト実行のあれこれ
- gotestsumを使ったGitHub Actionsでのレポート表示の方法
- gotestsumとoctocovを使ったGitHub Actionsでのテストカバレッジの表示方法
対象読者
- Goのテスト結果をもう少しリッチにしたいと実は思っている人
- Goのテストは白文字出力で十分、むしろ白文字が良いと思ってる人
- GitHub ActionsのようなCI環境でGoのテスト結果をレポートとして表示させたい人
- GitHub ActionsのようなCI環境でPRのコメントなどにテストカバレッジを表示させたい人
gotestsumについて
gotestsumはgo test -json
を実行し、その結果をフォーマットして表示するCLIツールです。ローカルでの実行にもCI環境での実行でも使用できるように設計されており、以下のような著名なプロジェクトでも利用されています。
- kubernetes
- hashicorp/vault
- prometheus
- minikube
- containerd
- docker/cli
...etc
インストールはgo install
でできます。
go install gotest.tools/gotestsum@latest
% gotestsum --version
> gotestsum version dev
gotestsumを使ったテスト実行
とりあえずテストを実行できるように簡単な関数とテストを用意します。
go mod gotestsum-demo
package calculate
func Add(x, y int) int {
return x + y
}
package calculate_test
import (
"gotestsum-demo/calculate"
"testing"
)
func TestAdd(t *testing.T) {
t.Parallel()
tests := map[string]struct {
x, y, expected int
}{
"1+1": {x: 1, y: 1, expected: 2},
"2+1": {x: 2, y: 1, expected: 3},
"1-1": {x: 1, y: -1, expected: 0},
"1+2": {x: 1, y: 2, expected: 3},
"1+0": {x: 1, y: 0, expected: 1},
"10+10": {x: 10, y: 10, expected: 20},
}
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
t.Parallel()
if result := calculate.Add(tt.x, tt.y); result != tt.expected {
t.Errorf("expected %d, but %d\n", tt.expected, result)
}
})
}
}
ではgotestsum
を使ってテストを実行してみましょう。gotestsum
のみで実行するとgo test ./...
と同じ範囲で実行されます。--
の後に通常のgo test
の後に続くパスやオプションを指定して実行することも可能です。
% gotestsum
✓ calculate (275ms)
DONE 7 tests in 0.893s
フォーマットを変更して出力する
gotestsum
は以下のフォーマットをサポートしています。
-
dots
ドットで結果を出力 -
dots-v2
実験的ドット出力 -
pkgname
(default) パッケージごとに結果を出力 -
pkgname-and-test-fails
パッケージごとに失敗したテストも出力 -
testname
テスト名ごとに結果を出力 -
testdox
gotestdoxのフォーマットを使用して出力 -
github-actions
GitHub ActionsのLog groupingとともにtestnameのフォーマットで出力 -
standard-quiet
go test
と同等の出力 -
standard-verbose
go test -v
と同等の出力
ここではいくつかのフォーマットを紹介します。
dots
testname
testdox
個人的にはテーブル駆動テストで実行するサブテストの結果まで見たいと思っているのでtestname
やtestdox
のフォーマットが見やすくていいなと思っています。
ちなみに、pkgname
やtestdox
はアイコンを変更することもできます。
default the original unicode (✓, ∅, ✖)
hivis higher visibility unicode (✅, ➖, ❌)
text simple text characters (PASS, SKIP, FAIL)
codicons requires a font from https://www.nerdfonts.com/ ( )
octicons requires a font from https://www.nerdfonts.com/ ( )
emoticons requires a font from https://www.nerdfonts.com/ ( )
codicons
、octicons
、emoticons
はNerd Fontsが必要になります。
最も実行速度が遅いテストを表示する
gotestsum
は--post-run-command
オプションを指定することでテスト実行後に指定の処理を実行することができます。また、--jsonfile
や--junitfile
を指定することでテスト結果をレポートとしてファイルに出力することが可能です。
これらのオプションを組み合わせるとテスト結果の出力に実行速度が最も遅いテストを表示することが可能です。以下のコマンドは実行速度が遅いテストを上から5つ表示するコマンドです。
% gotestsum \
--jsonfile tmp.json.log \
--post-run-command "bash -c '
echo; echo Slowest tests;
gotestsum tool slowest --num 5 --jsonfile tmp.json.log'"
✓ calculate (10.241s)
DONE 10 tests in 10.814s
Slowest tests
gotestsum-demo/calculate Test10S 10s
gotestsum-demo/calculate Test5S 5s
gotestsum-demo/calculate Test3S 3s
gotestsum-demo/calculate TestAdd/10+10 0s
gotestsum-demo/calculate TestAdd/1+0 0s
テストの実行完了を通知する
上記で紹介した--post-run-command
オプションを使用することでテストの完了を通知することも可能です。Macの場合、デスクトップ通知にterminal-notifierを使用します。
brew install terminal-notifier
terminal-notifier -version
> terminal-notifier 2.0.0.
次に以下のexampleの通知プログラムをインストールします。
go install gotest.tools/gotestsum/contrib/notify
以下のコマンドのようにテスト実行後に実行するコマンドとしてnotify
を指定するとテスト完了後にデスクトップ通知を飛ばすことができます。
gotestsum --post-run-command notify
ソースコードを監視してテストを実行する
--watch
オプションを使用することでカレントディレクトリ配下の全てのgoファイルを関監視しファイルが変更されるたびにテストを実行することが可能です。
gotestsum --format testdox --watch
ここまでのまとめ
ほかにも失敗したテストを再度実行するようなオプションがあったりと便利な機能があるgotestsum
ですが最低限ローカルで実行したテストの結果出力を見やすくするだけでも十分導入価値があるように感じます。
go test
の代わりに使う場合は毎回formatを指定したりするのは面倒なので以下のようなエイリアス設定を私はしています。
alias gotest="gotestsum --format testdox"
alias gotestv="gotestsum --format standard-verbose"
alias gotestw="gotestsum --format testdox --watch"
gotest -- -count=1 ./...
次にCI環境でのgotestsum
の利用について見ていきましょう。
GitHub Actionsでgotestsumを使う
今回はGitHub Actionsでgotestsum
を使っていきたいと思います。gotesetsum
を使うためにGitHub Actionsのワークフローのステップ内で以下のようにしてインストールしていきます。毎回インストールするのも効率が悪いのでactions/cache
を使ってキャッシュしています。
- name: Cache gotestsum
id: gotestsum-cache
uses: actions/cache@v4
with:
path: ${{ env.GOTESTSUM_BIN}}
key: ${{ runner.os }}-go-gotestsum
- name: Install gotestsum
if: ${{ steps.gotestsum-cache.outputs.cache-hit != 'true' }}
run: GOBIN="${{ env.GOTESTSUM_BIN }}" go install gotest.tools/gotestsum@latest
インストールできたら以下のようにしてワークフロー内でテストを実行できます。
- name: Run tests and generate JUnit report, test coverage
run: "${{ env.GOTESTSUM_BIN }}/gotestsum --format testdox
テストレポートを表示する
ここまででCI環境でgotestsum
を使用してテストを実行することができるようになりましたがただ実行するだけであればわざわざgotestsum
をインストールする意味はあまりありません。
既に説明しましたがgotestsum
にはJUnit形式のレポートを出力することができるため、GitHub Actionsでテストレポートを見れるよにしていきましょう。
- name: Cache gotestsum
id: gotestsum-cache
uses: actions/cache@v4
with:
path: ${{ env.GOTESTSUM_BIN}}
key: ${{ runner.os }}-go-gotestsum
- name: Install gotestsum
if: ${{ steps.gotestsum-cache.outputs.cache-hit != 'true' }}
run: GOBIN="${{ env.GOTESTSUM_BIN }}" go install gotest.tools/gotestsum@latest
- name: Run tests and generate JUnit report, test coverage
- run: "${{ env.GOTESTSUM_BIN }}/gotestsum --format testdox
+ run: "${{ env.GOTESTSUM_BIN }}/gotestsum --junitfile report.xml --format testdox"
+ - name: Test Report Summary
+ if: success() || failure()
+ uses: dorny/test-reporter@v1
+ with:
+ name: Tests
+ path: "*.xml"
+ reporter: java-junit
出力したレポートファイルはdorny/test-reporterを使用してGitHub Actionsから見れるようにしました。類似のアクションとしてpublish-unit-test-result-actionというものもあるようなので好きなものを使ってください。
CIのワークフローが成功すると以下のようなレポートが作成されます。
テストカバレッジをPRのコメントに書き込む
せっかくなのでGitHub Actionsを使ってテストのカバレッジを見れるようにしてみましょう。よくあるケースとしてPRのコメントにカバレッジレポートを追加するというものがあるようなのでそれをgotestsum
を使ってやっていきます。
と言ってもgotestsum
の機能というよりはgo test
の機能としてカバレッジを出力することができるためそれを使ってカバレッジを出力します。
- name: Run tests and generate JUnit report, test coverage
- run: "${{ env.GOTESTSUM_BIN }}/gotestsum --junitfile report.xml --format testdox"
+ run: "${{ env.GOTESTSUM_BIN }}/gotestsum --junitfile report.xml --format testdox -- -cover -coverprofile=coverage.out ./..."
これでcoverage.out
というファイルにカバレッジを出力することができました。
あとは出力したファイルを使い良い感じにPRのコメントに追加できれば良く、いろいろ調べたのですが最終的にk1LoW/octocov-actionを使用させていただきました。
似たようなactionは他にもあったのです1番シンプルで使いやすそうだったため採用させていただきました。
octocov
をGitHub Actionsで使うにはまず、以下のような設定ファイルを作成します。
coverage:
badge:
path: docs/coverage.svg
push:
if: is_default_branch
codeToTestRatio:
code:
- '**/*.go'
- '!**/*_test.go'
test:
- '**/*_test.go'
badge:
path: docs/ratio.svg
testExecutionTime:
badge:
path: docs/time.svg
diff:
datastores:
- artifact://${GITHUB_REPOSITORY}
comment:
if: is_pull_request
report:
datastores:
- artifact://${GITHUB_REPOSITORY}
summary:
if: true
設定ファイルが書けたらワークフローに以下の1文を追記します。
- uses: k1LoW/octocov-action@v1
これでワークフローが実行されると以下のようなコメントをPRに記載することができます。
octocov
はカバレッジとテストの実行時間とコードに対するテスト比率の3つを出力してくれますし、前回の結果をもとに差分を出力してくれたりもします。1番いいなと思ったのはそれぞれのbadgeを作成してくれてpushまでしてくれるのでREADMEなどに記載するのがとても簡単です。
最終的なワークフローは以下のようになりました。
最終的なワークフロー
name: Go test with report
on:
push:
branches:
- main
pull_request:
defaults:
run:
shell: bash
jobs:
test-with-report:
runs-on: ubuntu-latest
permissions:
contents: write
actions: read
checks: write
pull-requests: write
env:
GOTESTSUM_BIN: "${{ github.workspace }}/.tmp/gotestsum/bin"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- name: Cache gotestsum
id: gotestsum-cache
uses: actions/cache@v4
with:
path: ${{ env.GOTESTSUM_BIN}}
key: ${{ runner.os }}-go-gotestsum
- name: Install gotestsum
if: ${{ steps.gotestsum-cache.outputs.cache-hit != 'true' }}
run: GOBIN="${{ env.GOTESTSUM_BIN }}" go install gotest.tools/gotestsum@latest
- name: Run tests and generate JUnit report, test coverage
run: "${{ env.GOTESTSUM_BIN }}/gotestsum --junitfile report.xml --format testdox -- -cover -coverprofile=coverage.out ./..."
- name: Upload test report and coverage
uses: actions/upload-artifact@v4
with:
name: junit-test-report-and-coverage
path: |
report.xml
coverage.out
- name: Test Report Summary
if: success() || failure()
uses: dorny/test-reporter@v1
with:
name: Tests
path: "*.xml"
reporter: java-junit
- uses: k1LoW/octocov-action@v1
おわりに
本記事では以下のことを紹介しました。
-
gotestsum
を使ったローカルテストの実行結果の表示をリッチにする方法 -
gotestsum
を使いテスト実行後に処理を挟んだり、ソースコードの監視をしてテストの実行などをする方法 - GitHub Actionsでテストレポートを表示する方法
- GitHub ActionsでPRのコメントにテストカバレッジを追加する方法
本記事を書くモチベーションとしてはローカル環境でのテストの実行結果を見やすくしたいというものでしたがgotestsum
を使うことでその目的は達成することができました。
もし、Goのテストに満足していない方がいましたらgotestsum
を試してみてください!
今回は以上です🐼
(おまけ) 本当にテスト結果を見やすくする必要はあるのか?
調べてる中でgotestsum以外にrichgoというツールも見つけました。こちらはテスト結果の出力を解析して見た目を調整するような実装をしており、シンプルにGoのテスト結果をリッチに出力するためのツールなようです。
始めはこちらも試してみようかなと思ったのですがリポジトリを見てみるとREADMEに以下のような記載がありました。(DeepLで翻訳しています。翻訳がおかしいところは少し修正しています。)
何年か前から、richgoは使っていない。 今となっては、テスト出力の見た目をちょっといじったくらいでは、あまり効果を感じない。 また、richgoはgo testの標準出力を解析して調整するという稚拙な方法である。 というわけで、richgoは使わないで、純粋なgo testに慣れて、出力からエラーを見つける能力を鍛え、必要であれば公式go testの改良に貢献することをお勧めします。 私が純粋なGoを信頼しすぎていると思われるかもしれませんが、これは私の正直な気持ちです。
Goのテスト結果の出力をリッチにさせたいと思いOSSを作り、使い続けてきた結果、OSS作者自身効果を感じなくなったため使わなくなったとのことです。
Goのテスト結果をリッチにしたほうが良いのか。素のテスト結果に慣れたほうがいいのか。こちらに関しては意見が分かれそうです。
個人的にはテストは楽しく書くという持論があるのと見た目から入るタイプなのでテストの結果表示なんかも綺麗にフォーマットされて出力された方がテンションが上がるなと思っています。
なのでgotestsum
のようなツールを導入することでテストを楽しく書けるようになるなら導入すればいいのではないかなぁと思ったりします。
(しかし、自分で作ってスター数もそれなりについているOSSをもう使わなくなったって言えるのすごいなぁ)
Discussion