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

ブログ

333.png

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@latest

gotestを使うと、例えば、

PASS
ok      path/to/api    0.511s

といったテスト成功時の出力を緑色で表示できます。

これは便利です。

しかし、今回の問題は解決しませんでした。

no tests to runの場合でも、

testing: warning: no tests to run
PASS
ok      path/to/api    0.511s

となり、PASSokが緑色になります。

つまり、

テストが実行されていないのに、成功結果として緑色に表示される

という状態です。

gotestはあくまでgo testの出力をカラーリングするツールなので、

PASS → 緑
ok   → 緑
FAIL → 赤

という判断になります。

testing: warning: no tests to runが出ているかどうかを考慮して、PASSokの色を変えているわけではありません。


そこでシェルラッパーを作る

今回必要なのは、単純なカラーリングではありません。

以下のようなルールにしたいと考えました。

通常のテスト

PASS
ok      path/to/api    0.555s
  • PASS → 緑
  • ok → 緑
  • exit code → 0

テストが1件も存在しない場合

fsdfsadfsadfsafsdafsdafasfasdfasdfsdafesting: warning: no tests to runs
PASS
ok      path/to/api    0.476s
✖ no tests to run
  • PASS → 緑にしない
  • 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

PASSokが緑色で表示されます。

存在しないテストを指定した場合

go-test -run TestMySampleFunction

結果は

testing: warning: no tests to run
PASS
ok path/to/api 0.476s
✖ no tests to run

となります。

この場合はPASSokを緑色にせず、最後に ✖ no tests to run を赤色で表示します。

さらに、

echo $?

を実行すると、

1

となります。


なぜ一度ファイルに保存するのか

最初はgo testの出力をteeなどでリアルタイムに表示しながら、PASSokをカラーリングする方法も考えました。

しかし、それでは、

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"

PASSokを緑色にしています。


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件以上実行されたことを確認する」という観点で、シンプルですが便利な改善だと思います。

一覧へ戻る