これは、なにをしたくて書いたもの?
こちらでBashスクリプトをテストする場合の選択肢を調べてみました。
Bashスクリプトのテスティングフレームワークを調べてみる - CLOVER🍀
この中だと、まずはBatsが入門によいのかなと思ったのでチュートリアルを試してみようと思います。
Bats
Batsのドキュメントはこちら。
Welcome to bats-core’s documentation! — bats-core 1 documentation
GitHub - bats-core/bats-core: Bash Automated Testing System
Batshは、Bash 3.2以上を対象にしたTAPに準拠したテスティングフレームワークです。
Bats (Bash Automated Testing System) is a TAP-compliant testing framework for Bash 3.2 or above.
Arch Linux、Alpine Linux、Debian、Fedora、Gentoo Linux、openSUSE、Ubuntu Linuxではパッケージマネージャーからのインストールが
可能です。
Installation — bats-core 1 documentation
ところで、今のBatsはオリジナルものではないみたいですね。1.0より前がオリジナルのようです。
Note: Bats versions pre 1.0 are from sstephenson’s original project.
つまり、今のBatsはフォークです。以下を見ると2017年にフォークされ、オリジナルのBatsは2021年にアーカイブされたことがわかります。
Bats-core: Bash Automated Testing System / Background
ドキュメントがまとまっているのはBats-Coreですが、以下のような追加モジュールもあるようです。
- bats-assert
- Bats共通のアサーションを提供するヘルパーライブラリー
- GitHub - bats-core/bats-assert: Common assertions for Bats
- bats-file
- 一般的なファイルシステム関連のアサーションとヘルパーを提供するライブラリー
- GitHub - bats-core/bats-file: Common filesystem assertions for Bats
bats-supportというものもありますが、これは開発者向けのものみたいですね。bats-assertとbats-fileが依存しています。
GitHub - bats-core/bats-support: Supporting library for Bats test helpers
開発者向けのドキュメントはこちら。
GitHub - bats-core/bats-docs: Shared Bats helper documentation
今回はこちらのチュートリアルを試してみます。
Tutorial — bats-core 1 documentation
環境
今回の環境はこちら。
$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 24.04.3 LTS Release: 24.04 Codename: noble $ uname -srvmpio Linux 6.8.0-79-generic #79-Ubuntu SMP PREEMPT_DYNAMIC Tue Aug 12 14:42:46 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux
Batsをインストールする
まずはBatsをインストールしましょう。
batsで検索すると
$ apt search bats
このあたりが見つかります。
bats/noble 1.10.0-1 all bash automated testing system bats-assert/noble 2.1.0-3 all Helper library providing common assertions for Bats bats-file/noble 0.4.0-1 all Helper library providing filesystem-related assertions for Bats bats-support/noble 0.3.0-4 all Supporting library to test bats helper libraries
最新のBatsは1.12.0のようですが、今回はパッケージマネージャーでインストールできる1.10.0を使うことにします。
ちなみに、1.10.0のドキュメントを見ると現在のものとは見た目が大きく変わっていて驚きます…。
Installation — bats-core 1 documentation
1.11.0でドキュメントが大きく変わったようです。今回は1.10のものを見ていくことにします。
インストール。
$ sudo apt install bats
バージョン。
$ bats --version Bats 1.10.0
ヘルプ。
$ bats --help Bats 1.10.0 Usage: bats [OPTIONS] <tests> bats [-h | -v] <tests> is the path to a Bats test file, or the path to a directory containing Bats test files (ending with ".bats") -c, --count Count test cases without running any tests --code-quote-style <style> A two character string of code quote delimiters or 'custom' which requires setting $BATS_BEGIN_CODE_QUOTE and $BATS_END_CODE_QUOTE. Can also be set via $BATS_CODE_QUOTE_STYLE --line-reference-format Controls how file/line references e.g. in stack traces are printed: - comma_line (default): a.bats, line 1 - colon: a.bats:1 - uri: file:///tests/a.bats:1 - custom: provide your own via defining bats_format_file_line_reference_custom with parameters <filename> <line>, store via `printf -v "$output"` -f, --filter <regex> Only run tests that match the regular expression --filter-status <status> Only run tests with the given status in the last completed (no CTRL+C/SIGINT) run. Valid <status> values are: failed - runs tests that failed or were not present in the last run missed - runs tests that were not present in the last run --filter-tags <comma-separated-tag-list> Only run tests that match all the tags in the list (&&). You can negate a tag via prepending '!'. Specifying this flag multiple times allows for logical or (||): `--filter-tags A,B --filter-tags A,!C` matches tags (A && B) || (A && !C) -F, --formatter <type> Switch between formatters: pretty (default), tap (default w/o term), tap13, junit, /<absolute path to formatter> --gather-test-outputs-in <directory> Gather the output of failing *and* passing tests as files in directory (if existing, must be empty) -h, --help Display this help message -j, --jobs <jobs> Number of parallel jobs (requires GNU parallel or shenwei356/rush) --parallel-binary-name Name of parallel binary --no-tempdir-cleanup Preserve test output temporary directory --no-parallelize-across-files Serialize test file execution instead of running them in parallel (requires --jobs >1) --no-parallelize-within-files Serialize test execution within files instead of running them in parallel (requires --jobs >1) --report-formatter <type> Switch between reporters (same options as --formatter) -o, --output <dir> Directory to write report files (must exist) -p, --pretty Shorthand for "--formatter pretty" --print-output-on-failure Automatically print the value of `$output` on failed tests -r, --recursive Include tests in subdirectories --show-output-of-passing-tests Print output of passing tests -t, --tap Shorthand for "--formatter tap" -T, --timing Add timing information to tests -x, --trace Print test commands as they are executed (like `set -x`) --verbose-run Make `run` print `$output` by default -v, --version Display the version number For more information, see https://github.com/bats-core/bats-core
Batsでテストを書く
それでは、チュートリアルに沿ってテストを書いていってみましょう。
Tutorial — bats-core 1 documentation
テスト対象のスクリプトはsrcに、テスト用のスクリプトはtestに配置するようなのでディレクトリーを作成。
$ mkdir {src,test}
あとはチュートリアルに沿って進めていきます。
最初のテストを書く
最初のテストを書いてみます。
ソースコードを作らないまま、テストだけ作成。
test/test.bats
@test "can run out script" { ./project.sh }
実行。もちろん失敗します。
$ bats test/test.bats test.bats ✗ can run out script (in test file test/test.bats, line 2) `./project.sh' failed with status 127 /path/to/test/test.bats: 行 2: ./project.sh: そのようなファイルやディレクトリはありません 1 test, 1 failure
src/project.sh
#!/bin/bash echo 'Hello World'
実行権限を付与します。
$ chmod +x src/project.sh
テストスクリプトからテスト対象のスクリプトを見る時はbatsコマンドを実行したディレクトリーが起点になるようなので、以下のように修正。
test/test.bats
@test "can run out script" { src/project.sh }
今度はテストをパスします。
$ bats test/test.bats test.bats ✓ can run out script 1 test, 0 failures
セットアップする
次はセットアップを行ってみます。
Tutorial / Let’s do some setup
テストスクリプトをこのように変更。
test/test.bats
setup() { DIR=$(cd $(dirname $BATS_TEST_FILENAME) >/dev/null 2>&1 && pwd) PATH="$DIR/../src:$PATH" } @test "can run our script" { project.sh }
setup関数内で、srcディレクトリーにパスを通すようにしました。
setup関数というのは各テストの前に実行される関数のようです。各テストの実行後に呼ばれる関数を定義する場合はteardownになるようです。
Writing tests / setup and teardown: Pre- and post-test hooks
そのテストスクリプトで定義されているすべてのテストの前および後で実行したい場合は、setup_fileとteardown_fileを使うようです。
$BATS_TEST_FILENAME
というのは、テストスクリプトの絶対ファイルパスです。
Writing tests / Special variables
実行。
$ bats test/test.bats test.bats ✓ can run our script 1 test, 0 failures
OKですね。
出力を扱う
Tutorial / Dealing with output
ひとまずsrc/project.sh
を以下のように変更し、
src/project.sh
#!/bin/bash echo 'Welcome to our project!' echo 'NOT IMPLEMENTED!' >&2 exit 1
exit 1
しているのでテストが失敗することを確認します。
$ bats test/test.bats test.bats ✗ can run our script (in test file test/test.bats, line 7) `project.sh' failed Welcome to our project! NOT IMPLEMENTED! 1 test, 1 failure
ここで、テストスクリプトを修正するのにbats-assertが必要になるのでインストールします。
$ sudo apt install bats-assert
インストールしたバージョンはこちら。
$ apt show bats-assert Package: bats-assert Version: 2.1.0-3 Priority: optional Section: universe/devel Origin: Ubuntu Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com> Original-Maintainer: Debian Bats team <team+bats@tracker.debian.org> Bugs: https://bugs.launchpad.net/ubuntu/+filebug Installed-Size: 58.4 kB Depends: bats-support Enhances: bats Homepage: https://github.com/bats-core/bats-assert Download-Size: 9,042 B APT-Manual-Installed: yes APT-Sources: http://archive.ubuntu.com/ubuntu noble/universe amd64 Packages Description: Helper library providing common assertions for Bats bats-assert provides various ready-made assertions that can be used to make Bats tests simpler to understand and to debug. . For example: . * assert_success: exit status is 0. * assert_output: output contains given content. * assert_line: a specific line of output contains given content.
ちなみに依存しているbats-supportもついてきます。
$ apt show bats-support Package: bats-support Version: 0.3.0-4 Priority: optional Section: universe/libdevel Origin: Ubuntu Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com> Original-Maintainer: Debian Bats team <team+bats@tracker.debian.org> Bugs: https://bugs.launchpad.net/ubuntu/+filebug Installed-Size: 25.6 kB Homepage: https://github.com/bats-core/bats-support Download-Size: 5,326 B APT-Manual-Installed: no APT-Sources: http://archive.ubuntu.com/ubuntu noble/universe amd64 Packages Description: Supporting library to test bats helper libraries bats-support is a supporting library providing common functions to test helper libraries written for Bats. . 特徴: . * error reporting * output formatting * language tools
テストスクリプトを以下のように修正。
test/test.bats
setup() { load '/usr/lib/bats/bats-support/load' load '/usr/lib/bats/bats-assert/load' DIR=$(cd $(dirname $BATS_TEST_FILENAME) >/dev/null 2>&1 && pwd) PATH="$DIR/../src:$PATH" } @test "can run our script" { run project.sh assert_output 'Welcome to our project!' }
bats-fileをロードする必要がありますが、このパスは以下のコマンドで確認しました。最後の.bash
は明示的に付与しなくても、load関数が
補ってくれます。
※ドキュメントではbats-supportのロードも必要だと書かれていますが、なくても動くようでした
$ dpkg -L bats-assert | grep load /usr/lib/bats/bats-assert/load.bash $ dpkg -L bats-support | grep load /usr/lib/bats/bats-support/load.bash
またproject.sh
の呼び出しがrunコマンド経由になっていますが、これはBatsの関数です。
Writing tests / run: Test other commands
run関数は実行したコマンドの終了ステータスや出力をグローバル変数に保存するコマンドです。こんなイメージですね。
@test "invoking foo with a nonexistent file prints an error" { run foo nonexistent_filename [ "$status" -eq 1 ] [ "$output" = "foo: no such file 'nonexistent_filename'" ] [ "$BATS_RUN_COMMAND" = "foo nonexistent_filename" ] }
assert_output関数は$output
に対してアサーションを行います。
bats-assert / Full Assertion API / assert_output
そしてこれを実行するとまだテストに失敗します。
$ bats test/test.bats test.bats ✗ can run our script (in test file test/test.bats, line 11) `assert_output 'Welcome to our project!'' failed -- output differs -- expected (1 lines): Welcome to our project! actual (2 lines): Welcome to our project! NOT IMPLEMENTED! -- 1 test, 1 failure
これはassert_output関数が$output
全体と比較しようとしているからですね。
ここでassert_output関数に--partialオプションを追加して、部分一致にします。
test/test.bats
setup() { load '/usr/lib/bats/bats-support/load' load '/usr/lib/bats/bats-assert/load' DIR=$(cd $(dirname $BATS_TEST_FILENAME) >/dev/null 2>&1 && pwd) PATH="$DIR/../src:$PATH" } @test "can run our script" { run project.sh assert_output --partial 'Welcome to our project!' }
今度はテストにパスします。
$ bats test/test.bats test.bats ✓ can run our script 1 test, 0 failures
その他
だいぶ長くなってきたのと雰囲気もわかってきたのでこれくらいにしようと思いますが、あとはこんな内容でした。
- teardown関数を使う
- skip関数を使ってテストをスキップする
- 共通のテストヘルパーを作成してload関数で呼び出す
- setup_file関数とteardown_file関数を使う
おわりに
Bashのテスティングフレームワーク、Batsをチュートリアルを使って試してみました。
シェルスクリプトのテストを書いたことがなかったのでちょっと戸惑いましたが、基本的なことはわかったかなと思います。
ただ今回のようにテスト対象のスクリプトがとても単純ならいいのですが、長いスクリプトが対象だとテストを書くのが大変だろうなと
思います。シェルスクリプトは、なんだかんだ言って副作用の塊になりやすいので…。