ShellScript
Bash
Linux
Linuxコマンド
bats

bats ファイルのグローバルスコープには正常終了するコマンドだけを書く

More than 1 year has passed since last update.


はじめに


  • Bash のテストフレームワーク bats に関する記事

  • bats ファイルのグローバルスコープ 1 に書いたコマンドが異常終了 ($? -ne 0) すると、bats も異常終了する話


bats とは?

Bash スクリプトのテストツールです。参考サイトを以下に挙げます。


起きた問題


bats コード

以下を行うだけのコードです。わかりやすくするために実際のテストコードは端折っています。


  • env.sh を読み込む

  • /tmp/buffer.txt を初期化する


test.bats

#!/usr/bin/env bats

source ./env.sh

function setup() {
: > /tmp/buffer.txt
}



実行結果

想定していた処理結果は以下です。


想定

$ ./test.bats 

0 tests, 0 failures
$ echo $?
0


しかし、実際は以下のように処理に失敗してしまい、テストも行われませんでした。


想定外

$ ./test.bats

$ echo $?
1


原因

source ./env.sh が原因でした。

具体的には、以下のように env.sh 内で開発環境か否かのチェックを行っていました。


env.shでダメだった箇所

hostname | grep 'dev'

if [ $? -eq 0 ]; then
# 開発環境向けの処理
else
# 本番環境向けの処理
fi

問題が発生するのは hostname | grep 'dev' の実行結果が 1 となるとき、すなわち開発環境でないときです。

ここで bats は、グローバルスコープに記述したコマンドが正常終了しなかった場合に、 bats 自体も異常終了して落ちてしまうようです。


解決策

2つ考えられます。


ラップする方法

異常終了するコマンドを if でくくり、異常終了をラップします。


ダメだったやり方

hostname | grep 'dev'

if [ $? -eq 0 ]; then
# 開発環境向けの処理
:


良さげなやり方

if hostname | grep 'dev'; then

# 開発環境向けの処理
:

後者の方がスマートでいいですね。


無理やりな方法

正常終了しないコマンドは || : で無理やり正常終了させたことにします。


test.bats

source ./env.sh || :


source が失敗した場合は、 : つまり「何もしない」コマンドが実行されて必ず正常終了を返します。

これによって、 source がどちらに転んでも正常終了を返す仕組みを作ります。

ただ、エラーハンドリングという観点でみると杜撰なので、前者の if を使った方法を好ましく思います。


まとめ


  • bats を使うときはコマンドの戻り値 $? を意識する

  • 異常終了しそうなコマンドは if でラップするか、無理やり正常終了化させることで bats の強制終了を回避する


所感


  • bats の思想に触れてみると、「スクリプト内のすべてのコマンドは 0 で終了すること」がシェルスクリプトの1つの理想形なのかな、と感じたりした





  1. 本記事では「setupteardown@test などのファンクション外」のことを指しています。もっとも、シェルスクリプトにおいてはどこでもグローバルと呼べそうな気もしますが…