go testの「no tests to run」に気づけない問題をシェルラッパーで解決する

go test実行時に色をつけたい
Goでテストを実行していて、少し気になったことがありました。
例えば、-runオプションで実行したいテストを指定します。
go test -run TestMySampleFunctionここで、TestMySampleFunctionというテストが実際には存在しなかった場合、次のような結果になります。
testing: warning: no tests to run
PASS
ok path/to/api 0.476s一見すると、
「テストが通った!」
ように見えます。
しかし実際には、指定したテストは1件も実行されていません。
これは、テスト名をタイプミスした場合などに非常に気づきにくい問題だと感じました。
そこで、go testの出力を少しだけ加工するシェルラッパーを作ってみました。
no tests to run が発生するケース
例えば、実際には次のようなテストが存在するとします。
func TestResolveOAuthCallbackURL(t *testing.T) {
// ...
}これに対して、間違ったテスト名を指定します。
go test -run TestMySampleFunctionすると、
testing: warning: no tests to run
PASS
ok path/to/api 0.476sとなります。
-runは指定した正規表現にマッチするテストだけを実行するため、マッチするテストが存在しなければ、テストそのものは実行されません。
それでもgo testは終了コード0で終了し、PASSと表示されます。
何が問題なのか
例えば、次のような変更をしたとします。
go test -run TestResolveOAuthCallbackURL本当は、
TestResolveOAuthCallbackURLを実行したかったのに、タイプミスして、
go test -run TestMySampleFunctionとしてしまったとします。
この場合、
testing: warning: no tests to run
PASSとなるため、「テストが成功した」と勘違いしてしまう可能性があります。
gotestを試してみる
go testの出力を見やすくする、gotestというツールがあります
インストールは以下です。
go install github.com/rakyll/gotest@latestgotestを使うと、例えば、
PASS
ok path/to/api 0.511sといったテスト成功時の出力を緑色で表示できます。
これは便利です。
しかし、今回の問題は解決しませんでした。
no tests to runの場合でも、
testing: warning: no tests to run
PASS
ok path/to/api 0.511sとなり、PASSとokが緑色になります。
つまり、
テストが実行されていないのに、成功結果として緑色に表示される
という状態です。
gotestはあくまでgo testの出力をカラーリングするツールなので、
PASS → 緑
ok → 緑
FAIL → 赤という判断になります。
testing: warning: no tests to runが出ているかどうかを考慮して、PASSやokの色を変えているわけではありません。
そこでシェルラッパーを作る
今回必要なのは、単純なカラーリングではありません。
以下のようなルールにしたいと考えました。
通常のテスト
PASS
ok path/to/api 0.555sPASS→ 緑ok→ 緑- exit code →
0
テストが1件も存在しない場合
fsdfsadfsadfsafsdafsdafasfasdfasdfsdafesting: warning: no tests to runs
PASS
ok path/to/api 0.476s
✖ no tests to runPASS→ 緑にしないok→ 緑にしないno tests to run→ 赤- exit code →
1
こうすれば、単に見た目を変えるだけではなく、テストが実行されていないことをエラーとして扱えます。
go-testを作る
~/bin/go-testというスクリプトを作ります。
mkdir -p ~/bin
vim ~/bin/go-test内容は以下です。
#!/bin/bash
output=$(mktemp)
trap 'rm -f "$output"' EXIT
go test "$@" >"$output" 2>&1
status=$?
if grep -q "testing: warning: no tests to run" "$output"; then
cat "$output"
printf '\033[1;31m✖ no tests to run\033[0m\n'
exit 1
fi
sed \
-e $'s/^PASS$/\033[1;32mPASS\033[0m/' \
-e $'s/^ok\t/\033[1;32mok\t\033[0m/' \
"$output"
exit "$status"実行権限を付けます。
chmod +x ~/bin/go-test~/binがPATHに含まれていない場合は、~/.zshrcなどに追加します。
export PATH="$HOME/bin:$PATH"設定を反映します。
source ~/.zshrc実行してみる
これまで、
go test -run TestResolveOAuthCallbackURLとしていたところを、
go-test -run TestResolveOAuthCallbackURLとします。
正常にテストが実行された場合
| PASS ok path/to/api 0.555s |
PASSとokが緑色で表示されます。
存在しないテストを指定した場合
go-test -run TestMySampleFunction結果は
| testing: warning: no tests to run PASS ok path/to/api 0.476s ✖ no tests to run |
となります。
この場合はPASSとokを緑色にせず、最後に ✖ no tests to run を赤色で表示します。
さらに、
echo $?を実行すると、
1となります。
なぜ一度ファイルに保存するのか
最初はgo testの出力をteeなどでリアルタイムに表示しながら、PASSやokをカラーリングする方法も考えました。
しかし、それでは、
testing: warning: no tests to run
PASS
ok ...という出力を見た時点では、まだno tests to runを検出できていません。
そこで、
go test "$@" >"$output" 2>&1として、一度すべての出力をファイルに保存します。
その後、
if grep -q "testing: warning: no tests to run" "$output"; thenでno tests to runが存在するかを確認します。
存在する場合は、
cat "$output"
printf '\033[1;31m✖ no tests to run\033[0m\n'
exit 1として、元の出力をそのまま表示しつつ、エラーとして終了します。
存在しない場合だけ、
sed \
-e $'s/^PASS$/\033[1;32mPASS\033[0m/' \
-e $'s/^ok\t/\033[1;32mok\t\033[0m/' \
"$output"でPASSとokを緑色にしています。
ANSIカラーコードについて
今回使用しているのはANSIエスケープシーケンスです。
例えば以下のようなコードで指定します
| 太字の赤 | '\033[1;31m' |
| 太字の緑 | '\033[1;32m' |
| カラーのリセット | '\033[0m' |
まとめ
今回のポイントは、単にgo testの表示をカラフルにすることではありません。
問題なのは、
testing: warning: no tests to run
PASSという、一見すると成功したように見える状態です。
rakyll/gotestのようなカラーリングツールを使っても、
PASS → 緑
ok → 緑となってしまうため、今回の問題は解決できませんでした。
そこで、go testを薄いシェルラッパーで包み、
go test
│
▼
no tests to run ?
│ │
Yes No
│ │
▼ ▼
赤色表示 PASS → 緑
exit 1 ok → 緑というルールにしました。
これなら、テスト名をタイプミスしてしまった場合でも、
go-test -run TestMySampleFunctionを実行した時点で、
✖ no tests to runと明確に気づくことができます。
「PASSと表示されたから安心」ではなく、「実際にテストが1件以上実行されたことを確認する」という観点で、シンプルですが便利な改善だと思います。