CLOVER🍀

That was when it all began.

Bashのテスティングフレームワーク、Batsを試す

これは、なにをしたくて書いたもの?

こちらでBashスクリプトをテストする場合の選択肢を調べてみました。

Bashスクリプトのテスティングフレームワークを調べてみる - CLOVER🍀

この中だと、まずはBatsが入門によいのかなと思ったのでチュートリアルを試してみようと思います。

Bats

Batsのドキュメントはこちら。

Welcome to bats-core’s documentation! — bats-core 1 documentation

GitHubリポジトリーはこちら。

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 LinuxDebianFedoraGentoo LinuxopenSUSEUbuntu 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-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}

あとはチュートリアルに沿って進めていきます。

最初のテストを書く

最初のテストを書いてみます。

Tutorial / Your first 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ディレクトリー内にスクリプトを作成。

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
その他

だいぶ長くなってきたのと雰囲気もわかってきたのでこれくらいにしようと思いますが、あとはこんな内容でした。

おわりに

Bashのテスティングフレームワーク、Batsをチュートリアルを使って試してみました。

シェルスクリプトのテストを書いたことがなかったのでちょっと戸惑いましたが、基本的なことはわかったかなと思います。

ただ今回のようにテスト対象のスクリプトがとても単純ならいいのですが、長いスクリプトが対象だとテストを書くのが大変だろうなと
思います。シェルスクリプトは、なんだかんだ言って副作用の塊になりやすいので…。