CLOVER🍀

That was when it all began.

BatsでBash関数のテストを書く&runコマンドを詳しく見てみる

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

Bash関数のテストをBatsで書くには?と思って調べてみたのですが、なんのことはなくrunで実行すればよいだということが
わかりました。

せっかくなので練習を兼ねて書いてみます。

またrunコマンドについても詳しく見ておきましょう。

runでテスト対象の関数を呼び出す

前にBatsを使った時はrunコマンドでシェルスクリプトを呼び出していたのですが、関数についてもrunコマンドを使えば
よいみたいです。

あらためてrun関数の説明を見てみます。

run関数は、そもそもコマンドを実行する関数だと書かれていますね。

Many Bats tests need to run a command and then make assertions about its exit status and output. Bats includes a run helper that invokes its arguments as a command, saves the exit status and output into special global variables, and then returns with a 0 status code so you can continue to make assertions in your test case.

Writing tests / run: Test other commands

終了ステータスと出力を特別なグローバル変数に格納します。

特別なグローバル変数とは以下の4つです。

  • $status … コマンドのステータスコード
  • $output … コマンドの標準出力と標準エラー出力
  • $BATS_RUN_COMMAND … 実行したコマンドと引数
  • $lines … 出力の個々の行を格納した配列。デフォルトでは空行は省略される

出力についてですが、--separate-stderrを使うと標準出力と標準エラー出力$output$stderr、   ${lines[@]}${stderr_lines[@]}に分割できるようです。

なお、runコマンドはサブシェルで実行されます。

あまり使わない気もしますが、パイプを使いたい場合はサブシェルを使います。

run bash -c "command args ... | jq -e '.limit == 42'"

あとは簡単に使っていってみます。

環境

今回の環境はこちら。

$ bash --version
GNU bash, バージョン 5.2.21(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2022 Free Software Foundation, Inc.
ライセンス GPLv3+: GNU GPL バージョン 3 またはそれ以降 <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Bats。

$ bats --version
Bats 1.10.0


$ apt search ^bats.*
ソート中... 完了
全文検索... 完了
bats/noble,noble,now 1.10.0-1 all [インストール済み]
  bash automated testing system

bats-assert/noble,noble,now 2.1.0-3 all [インストール済み]
  Helper library providing common assertions for Bats

bats-file/noble,noble,now 0.4.0-1 all [インストール済み]
  Helper library providing filesystem-related assertions for Bats

bats-support/noble,noble,now 0.3.0-4 all [インストール済み、自動]
  Supporting library to test bats helper libraries

BatsでBash関数のテストを書く

まずはテスト対象を用意。

src/func.sh

#!/bin/bash

greeting() {
    local name=$1

    echo "Hello ${name}!!"

    return 0
}

always_fail() {
    return 1
}

multiple_line() {
    cat << EOS
hello
world
EOS
}

少しわざとらしいですが、runコマンドの確認を兼ねています。

テストコードの雛形はこちら。

test/func.bats

setup() {
    bats_load_library bats-support
    bats_load_library bats-assert

    DIR=$(cd "$(dirname "$BATS_TEST_FILENAME")" > /dev/null 2>&1 && pwd)
    # shellcheck disable=SC1091
    source "${DIR}/../src/func.sh"
}

# ここにテストを書く

引数付きの関数呼び出し。

@test "test greeting function" {
    run greeting 'World'

    assert_success
    assert_output 'Hello World!!'
}

runコマンドのグローバル変数は使っていません。

使った場合はこんな感じですね。

@test "test greeting function2" {
    run greeting 'Bats'

    assert_equal "$status" 0
    assert_equal "$output" 'Hello Bats!!'
    assert_equal "$BATS_RUN_COMMAND" 'greeting Bats'
}

0で終了しない関数の場合。

@test "test always fail function" {
    run always_fail

    assert_failure
    assert_failure 1
    assert_equal $status 1
}

複数行を出力する関数のテスト。

@test "test multiple line" {
    multiple_line
    run multiple_line

    assert_success

    assert_output --partial "hello"
    assert_output --partial "world"

    assert_equal "${lines[0]}" "hello"
    assert_equal "${lines[1]}" "world"
    assert_equal "${lines[*]}" "hello world"
}

こんなところでしょうか。

アサーションはこちらを確認しましょう。

GitHub - bats-core/bats-assert at v2.1.0

おわりに

BatsでBash関数のテストを書いてみました。

これ自体はふつうにrunコマンドを使えばよかったのでなんのことはなかったのですが、せっかくなのでrunコマンドに
ついてもちゃんと見ておきました。

シェルスクリプトを書くのは得意ではない割に書く機会が妙に増えている気がするので、慣れておきたいですね…。