web-dev-qa-db-ja.com

シェルスクリプトの単体テスト

私が長年にわたって取り組んできたほとんどすべての製品には、何らかのレベルのシェルスクリプト(またはWindowsのバッチファイル、PowerShellなど)が関係しています。コードの大部分をJavaまたはC++で記述しましたが、シェルスクリプトを使用した方がよい統合タスクまたはインストールタスクが常に存在するように見えました。

したがって、シェルスクリプトは出荷されたコードの一部になるため、コンパイルされたコードと同様にテストする必要があります。 shunit2 など、世の中にあるシェルスクリプトユニットテストフレームワークの経験がある人はいますか?今のところ、主にLinuxシェルスクリプトに興味があります。テストハーネスが他のxUnitフレームワークの機能と使いやすさをいかにうまく複製できるか、CruiseControlやHudsonなどの継続的なビルドシステムとの統合がいかに簡単かを知りたいと思います。

66
gareth_bowles

更新2019-03-01:私の好みは bats nowです。私は数年間小さなプロジェクトでそれを使用しました。簡潔で簡潔な構文が好きです。 CI/CDフレームワークと統合していませんが、終了ステータスはスイートの全体的な成功/失敗を反映しており、以下に説明するshunit2よりも優れています。


前の回答:

Linux環境のJava/Ruby Webアプリケーションに関連するシェルスクリプトに shunit2 を使用しています。使いやすく、他のxUnitフレームワークから大きく逸脱していません。

CruiseControlやHudson/Jenkinsとの統合を試みたことはありませんが、他の手段で継続的な統合を実装する際に、これらの問題に遭遇しました。

  • 終了ステータス:テストスイートが失敗すると、shunit2はゼロ以外の終了ステータスを使用して失敗を通知しません。そのため、shunit2の出力を解析してスイートの合否を判断するか、shunit2を変更して、継続的な統合フレームワークが期待するように動作し、終了ステータスを介して合否を通信する必要があります。
  • XMLログ:shunit2は、結果のJUnitスタイルのXMLログを生成しません。
49
Pete TerMaat

[〜#〜] bats [〜#〜] 。誰も言及しなかった理由を疑問に思う。最新かつ [〜#〜] tap [〜#〜] に準拠しています。

説明する:

#!/usr/bin/env bats

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

実行:

$ bats addition.bats
 ✓ addition using bc

1 tests, 0 failures
27
phil pirozhkov

@ blake-mizeranyによるまとめは素晴らしいと思います。将来的にそれを利用する必要がありますが、単体テストを作成するための私の「貧乏人」アプローチは次のとおりです。

  • テスト可能なすべてを関数として分離します。
  • 関数を外部ファイルに移動します。たとえば、functions.shおよびsource itをスクリプトに移動します。この目的でsource `dirname $0`/functions.shを使用できます。
  • functions.shの最後に、以下のif条件にテストケースを埋め込みます。

    if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    fi
    
  • テストは、関数のリテラルコールと、それに続く終了コードと変数値の簡単なチェックです。以下のような単純なユーティリティ関数を追加して、簡単に記述できるようにします。

    function assertEquals()
    {
        msg=$1; shift
        expected=$1; shift
        actual=$1; shift
        if [ "$expected" != "$actual" ]; then
            echo "$msg EXPECTED=$expected ACTUAL=$actual"
            exit 2
        fi
    }
    
  • 最後に、functions.shを直接実行してテストを実行します。

アプローチを示すサンプルを次に示します。

    #!/bin/bash
    function adder()
    {
        return $(($1+$2))
    }

    (
        [[ "${BASH_SOURCE[0]}" == "${0}" ]] || exit 0
        function assertEquals()
        {
            msg=$1; shift
            expected=$1; shift
            actual=$1; shift
            /bin/echo -n "$msg: "
            if [ "$expected" != "$actual" ]; then
                echo "FAILED: EXPECTED=$expected ACTUAL=$actual"
            else
                echo PASSED
            fi
        }

        adder 2 3
        assertEquals "adding two numbers" 5 $?
    )
20
haridsv

まとめ: http://bmizerany.github.com/roundup/

READMEで詳細に説明している記事にリンクがあります。

17
Blake Mizerany

roundup および shunit2 に加えて、シェルユニットテストツールの概要にも assert.sh および shelltestrunner が含まれていました。

筆者はshunit2(一部は主観的)についての総括著者の批判にほぼ同意するため、ドキュメントと例を見てからshunit2を除外しました。ただし、jUnitの使用経験があることはおなじみのようです。

私の意見では、shelltestrunnerは、テストケースの定義に単純な宣言構文を使用しているため、私が見た中で最も独創的なツールです。いつものように、あらゆるレベルの抽象化は、ある程度の柔軟性を犠牲にしてある程度の利便性を提供します。とはいえ、シンプルさは魅力的ですが、主にセットアップ/ tearDownアクションを定義する方法がないために、ツールが私が持っているケースに限定しすぎていることがわかりました(たとえば、テスト前に入力ファイルを操作し、テスト後に状態ファイルを削除します)など)。

私は、assert.shが出力または終了ステータスのどちらかしかアサートできないのに対し、両方が必要だと最初は少し混乱していました。切り上げを使用していくつかのテストケースを記述するのに十分な長さ。しかし、すぐにまとめを見つけましたset -eモードは、stdoutに加えて結果を通信する手段として、ゼロ以外の終了ステータスが予期される場合があり、このモードではテストケースが失敗します。 サンプルの1つ は解を示します。

status=$(set +e ; rup roundup-5 >/dev/null ; echo $?)

しかし、ゼロ以外の終了ステータスと出力の両方が必要な場合はどうなりますか?もちろん、set +e呼び出し前およびset -eの後またはset +eテストケース全体。しかし、それはまとめの原則に反します 「すべてはアサーションです」 。だから、私はこのツールに対して働き始めているように感じました。

それまでに、このような複合式でtestを渡すことができるので、終了ステータスまたは出力のいずれかのみをアサートできるassert.shの「欠点」が実際には問題ではないことに気付きました。

output=$($tested_script_with_args)
status=$?
expected_output="the expectation"
assert_raises "test \"$output\" = \"$expected_output\" -a $status -eq 2"

私のニーズは本当に基本的なものだったので(一連のテストを実行し、すべてがうまくいったか失敗したかを表示します)、私はassert.shのシンプルさが気に入ったので、それを選択しました。

8
vilpan

Jenkinsのxml結果を生成でき、実際には何も見つからない、Shellの簡単な単体テストフレームワークを探した後、私はそれを書きました。

Sourceforgeにあります-プロジェクトの名前はjshuです。

http://sourceforge.net/projects/jsh

2
D. Scott

assert.sh libを試してみてください。非常に便利で使いやすいです

local expected actual
expected="Hello"
actual="World!"
assert_eq "$expected" "$actual" "not equivalent!"
# => x Hello == World :: not equivalent! 
2
Mark

最近、shellspecと呼ばれる新しいテストフレームワークをリリースしました。

shellspecはBDDスタイルのテストフレームワークです。 bash、dash、ksh、busyboxなどを含むPOSIX互換のシェルスクリプトで動作します。

もちろん、終了ステータスは仕様の実行結果を反映しており、TAP準拠のフォーマッターを備えています。

Specfileは自然言語に近く、読みやすく、またシェルスクリプト互換の構文です。

#shellcheck Shell=sh

Describe 'sample'
  Describe 'calc()'
    calc() { echo "$(($*))"; }

    It 'calculates the formula'
      When call calc 1 + 2
      The output should equal 3
    End
  End
End
1