シェルスクリプトのテストフレームワーク Bats を試してみた

シェルスクリプトのテストフレームワーク Bats を試してみた.

github.com

たまにコントリビュートをしている Amazon ECS (Elastic Container Service) のデプロイツール ecs-deploy のテストコードで Bats が使われているので,少し書いたことがあり,もっと詳しく調べてみようと思った.

github.com

Bats は現在もメンテナンスされている

もともとの Bats は sstephenson/bats にあり,現在は bats-core/bats-core に移っている.以下の Issue などを読むと,コミュニティの動きがわかる.

github.com

github.com

インストール

Bats は様々なインストール方法がある.まず,用意されている install.sh を試してみた.

$ git clone git@github.com:bats-core/bats-core.git
$ cd bats-core
$ sudo ./install.sh /usr/local
$ which bats
/usr/local/bin/bats

Mac の場合は brew でもインストールできる.

$ brew install bats

他にも,サードパーティだけど,npm でもインストールできる.

www.npmjs.com

サンプルコード

まず,README に以下のサンプルコード addition.bats が載っているので実行してみた.テストコードとしては bc で加算をするテストと,dc で逆ポーランド記法の計算をするテストコードが書かれている.

#!/usr/bin/env bats

@test "addition using bc" {
  result="$(echo 2+2 | bc)"
  [ "$result" -eq 4 ]
}

@test "addition using dc" {
  result="$(echo 2 2+p | dc)"
  [ "$result" -eq 4 ]
}

実行をすると,以下のような出力となる.なお,ターミナルで実行する場合は --pretty オプションがデフォルトになっている.

$ bats addition.bats
 ✓ addition using bc
 ✓ addition using dc

2 tests, 0 failures

テストがエラーになると,以下のような出力となる.

$ bats echo.bats
 ✗ echo
   (in test file echo.bats, line 5)
     `[ "${result}" -eq 1000 ]' failed

1 test, 1 failure

--tap オプションを付けると,TAP (Test Anything Protocol) フォーマットで出力される.TAP は今まで聞いたことがなかった.

$ bats --tap addition.bats
1..2
ok 1 addition using bc
ok 2 addition using dc

なお,複数の Bats を実行することもできる.

$ bats *.bats
 ✓ addition using bc
 ✓ addition using dc
 ✓ addition using bc
 ✓ addition using dc

4 tests, 0 failures

setup と teardown

多くのテストフレームワークで使える setupteardown も用意されている(テスト群の最初と最後にフックする).以下のように関数を定義して使う.

#!/usr/bin/env bats

setup() {
  touch bats.log
}

teardown() {
  rm bats.log
}

@test "echo with setup and teardown" {
  result="$(echo 100)"
  [ "${result}" -eq 100 ]
}

関数をテストする

Bats の実行方法がわかったので,次は functions.sh を用意して,引数に渡した文字列を大文字に変換する関数を実装した.

#!/bin/sh

#######################################
# 大文字に変換する
# Arguments:
#   $1 WORD
#######################################
upper_case() {
  echo $1 | tr '[:lower:]' '[:upper:]'
}

Bats は以下のようになる.関数をテストするので setupfunctions.sh を読み込み,Bats の run で関数を呼び出している.

#!/usr/bin/env bats

setup() {
  . functions.sh
}

@test "upper case for ruby" {
  run upper_case ruby
  [ "${status}" -eq 0 ]
  [ "${output}" == "RUBY" ]
}

run の結果は Bats の組み込み変数に入るため,これらをテスト結果として使う.

  • ${status} ... ステータスコード
  • ${output} ... 出力結果
  • ${lines} ... 出力結果(配列)
bats functions.bats
 ✓ upper case for ruby

1 test, 0 failures

Dockerized とは相性が悪そう

CircleCI 2.0 でテストを実行しようと思って,Bats を Dockerized したけど(Alpine と Amazon Linux),Bats を実行するコンテナにライブラリなどが必要になるため,例えば Bats のサンプルコードだと bcdc も入ってなく,エラーになる.ecs-deploy では,必要なライブラリを Dockerfile でインストールしている.

 ✗ addition using bc
   (in test file app/addition.bats, line 4)
     `result="$(echo 2+2 | bc)"' failed with status 127
   /tmp/bats.36.src: line 4: bc: command not found
 ✗ addition using dc
   (in test file app/addition.bats, line 9)
     `result="$(echo 2 2+p | dc)"' failed with status 127
   /tmp/bats.36.src: line 9: dc: command not found

2 tests, 2 failures

試しに Dockerized Bats を使って CircleCI 2.0 でテストできるようにしてみた.ただしこれは,ライブラリに依存しない状態で動いているので,参考程度という感じ.

github.com

まとめ

  • シェルスクリプトのテストフレームワーク Bats を試してみた
  • 運用系でシェルスクリプトを使う場面はまだまだあると思うので,テストコードが書けるとメンテナンスしやすくなる
  • シェルスクリプトの関数ごとにテストを書けるので,そこそこ柔軟にテストコードが書けそう