駅メモチームでエンジニアをしている id:Eadaeda です。シバンは #!/usr/bin/env
を使う派です。
皆さんはシェルスクリプト書いてますか? 環境構築、開発、テスト、ビルド、デプロイなどなど、一連の作業を自動化するための手段として時々出番があるんじゃないでしょうか。
ところでそのシェルスクリプト、テスト書いてますか?
シェルスクリプトのテスト
「シェルスクリプトのテスト〜?」って感じですよね。殆どの場合、一度書いてしまえばあんまり壊れることはないし別に…って感じですよね。わかります。実際開発環境のためにdocker compose up
するだけのスクリプトなら雑でもいいですよね。
でも、重要な役割をもつスクリプトならどうでしょう。例えばアプリケーションのエントリーポイントや、リリースビルド・デプロイのためのスクリプトなどが思いつきます。
こういうのはテストである程度保証されていれば安心じゃないですか?どうですか?書きたくなってきましたか?とりあえず一回書いてみませんか?
Bats/ShellSpecでシェルスクリプトのテストを書いてみよう
シェルスクリプトにもテストフレームワークがあります。たとえばBatsやShellSpecなどです。
今回は上記2つについて少しだけ紹介しようと思います。テスト対象のスクリプトは以下のものとします。
#!/usr/bin/env zsh if [[ "${1}" == "en" ]]; then echo "Hello World" elif [[ "${1}" == "" ]]; then echo "こんにちは 世界" fi
第一引数にen
を渡せば Hello World
を、何も渡さなければ こんにちは 世界
と出力するだけのスクリプトです。これを bin/hello-world.sh
として保存しておきます。
Bats
Batsは10年ぐらい前からあるそこそこ定番のテストフレームワークです。簡素に書けるのがいいところかなと思っています。
test/hello-world.bats
として以下の内容を保存します。
#!/usr/bin/env bats # 出力を検査するシンプルなテスト。シェルの構文っぽく書く @test "引数がないとき、こんにちは 世界が返されるべき" { result="$(./bin/hello-world.sh)" [ "$result" = "こんにちは 世界" ] } @test "引数がないとき、こんにちは 世界が返されるべき - 2" { # run を使うと $output に出力が格納される run ./bin/hello-world.sh [ "$output" = "こんにちは 世界" ] } # bats-core/bats-supportとbats-core/bats-assertを ./test/helpers 以下にcloneして読み込めば # 様々なヘルパーが使えるようになって便利 load 'helpers/bats-support/load' load 'helpers/bats-assert/load' @test "引数がenのとき、Hello Worldが返されるべき" { run ./bin/hello-world.sh en # runの出力が一致しているかを見るヘルパー assert_output 'Hello World' }
実行は bats
コマンドに渡してやればよいです
$ bats ./test/hello-world.bats hello-world.bats ✓ 引数がないとき、こんにちは 世界が返されるべき ✓ 引数がないとき、こんにちは 世界が返されるべき - 2 ✓ 引数がenのとき、Hello Worldが返されるべき 3 tests, 0 failures
setup/teardownも書くことができます
#!/usr/bin/env bats setup() { # ヘルパーのロードをsetupでやっちゃう load 'helpers/bats-support/load' load 'helpers/bats-assert/load' } @test "引数がenのとき、Hello Worldが返されるべき" { run ./bin/hello-world.sh en # setupでロードしてるのでヘルパーが使えちゃう assert_output 'Hello World' }
ShellSpec
ShellSpecはBDDな単体テストフレームワークです。RSpecとかJestみたいな書き味でテストを書いていくことができ、機能も豊富です。
まずはプロジェクトのセットアップです。最低限.shellspec
ファイルが必要となりますが、shellspec --init
で作成できるので、これを使うのが楽です。
$ shellspec --init create /path/to/pwd/.shellspec create /path/to/pwd/spec/spec_helper.sh
あとは spec/
以下にテストを書いていきます。今回、READMEのTypical directory structureにならってファイル名はspec/bin/hello-world_spec.sh
としました。内容は以下の通りです。なんだかプログラムっていうか普通の文章みたいになりますね。
Describe "bin/hello-world.shについて" Context "引数がないとき" It "こんにちは 世界が出力されるべき" When call ./bin/hello-world.sh The output should eq 'こんにちは 世界' End End Context "引数がenのとき" It "Hello Worldが出力されるべき" When call ./bin/hello-world.sh en The output should eq 'Hello World' End End End
テストの実行は shellspec --init
したディレクトリでshellspec
を実行するだけです
# --shell指定なしだと /bin/sh になる $ shellspec --shell zsh Running: /bin/zsh [zsh 5.8.1] .. Finished in 0.27 seconds (user 0.04 seconds, sys 0.04 seconds) 2 examples, 0 failures
まとめ
今回はシェルスクリプトのテストフレームワークであるBatsとShellSpecの触りだけご紹介しました。どちらを使うかはREADMEを読んでみて決めてみてくださいね。
以上です。