ユニットテストbashスクリプト


112

Javaコードのほかにいくつかのbashスクリプトが実行されているシステムがあります。壊れる可能性のあるすべてのものをテストしようとしていて、bashスクリプトが壊れる可能性があるので、テストしたいと思います。

問題は、bashスクリプトのテストが難しいことです。

bashスクリプトをテストする方法またはベストプラクティスはありますか?または、bashスクリプトの使用を中止して、テスト可能な代替ソリューションを探す必要がありますか?




既存のツールの概要:medium.com/wemake-services/...
sobolevn

回答:


48

実際には、ボーンベースのシェルスクリプト用のxUnitベースの単体テストフレームワークであるshunit2があります。私自身は使用していませんが、チェックする価値があるかもしれません。

同様の質問が以前に尋ねられました:


3
shunit2(バージョン2.1.6)はこれまで少し壊れていると断言できます。assertNullとassertNotNullは、直接値を入力しても機能しません。assertEqualsは正常に機能しますが、今は自分でロールする必要があると思います。
ラビリンス

@labyrinth、問題はこれのケースではなかったと確信していますか:github.com/kward/shunit2/issues/53「assertNullを正しく使用する方法?」
ビクターセルジャンコ2017年

1
@Victor二重引用符で十分注意していない可能性があります。まもなくshunit2またはbashの単体テストシステムが非常に役立つ役割に戻ります。もう一度やってみます。
ラビリンス

5
私は、ユーザー、時にはshunit2に貢献しています、と私はプロジェクトが健在2019年であることを確認することができます
アレックス・ハーヴェイ

31

ディスカッショングループから次の回答を得ました。

外部ファイルからプロシージャ(関数、名前が何であれ)をインポート(インクルードなど)することが可能です。それがテストスクリプトを作成するための鍵です。スクリプトを独立したプロシージャに分割し、実行中のスクリプトとテストスクリプトの両方にインポートできます。次に、実行中のスクリプトをできるだけシンプルにします。

この方法は、スクリプトの依存性注入に似ており、合理的に聞こえます。bashスクリプトを避け、テスト可能で曖昧でない言語を使用することをお勧めします。


4
賛成投票か反対投票かは
わかり

10
bashには何の問題もありませんが(私は多くのスクリプトを作成しました)、習得が難しい言語です。私の経験則では、スクリプトがテストを必要とするのに十分な大きさである場合、おそらく簡単にテストできるスクリプト言語に移るべきです。
Doug、

1
しかし、ユーザーのシェルから調達できるものが必要になる場合があります。シェルスクリプトに頼らずにそれをどのように行うかは、私にはわかりません
Itkovian '25

@Itkovian-たとえば、npmを使用して実行可能ファイルをパスにエクスポートできるため、ソーシングは必要ありません(npmパッケージをグローバルにインストールする必要があります)
Eliran Malka

1
bashを使用しないことについてのアドバイスに従います。:)
MaciejWawrzyńczuk18年

30

TAP準拠のBashテスト:Bash自動テストシステム

TAP(テストエニーシングプロトコル)は、テストハーネス内のテストモジュール間の単純なテキストベースのインターフェースです。TAPはPerlのテストハーネスの一部として開始されましたが、現在はC、C ++、Python、PHP、Perl、Java、JavaScriptなどで実装されています。


14
TAPとは何か、なぜ気にする必要があるかを明らかにすることは価値があります。それ以外の場合は、意味のないコピー/ペーストです
om-nom-nom

@ om-nom-nom:TAPサイトにリンクしました。
Janus Troelsen 2014

7
他の誰も不愉快なことを言っていなかったので:TAP =テスト何かプロトコル
JW。

9

Nikita Sobolevは、いくつかの異なるbashテストフレームワークを比較する優れたブログ投稿を書きました:Bashアプリケーションのテスト

せっかちな人のために:Nikitaの結論はBatsを使用することでしたが、Nikitaは、オリジナルのBatsプロジェクトが2013年以降積極的に維持されていないため、今後使用すると思われるBats-coreプロジェクトを逃したようです。


7

エポキシは、私は他のソフトウェアをテストするために主に設計されたBashのテストフレームワークであるが、私は同様に、試験のbashモジュールにそれを使用する、など自体カートン

主な利点は、比較的低いコーディングオーバーヘッド、無制限のアサーションネスト、検証するアサーションの柔軟な選択です。

Red Hatで使用されているフレームワークであるBeakerLibと比較したプレゼンテーションを行いました。


6

bashスクリプトをテストすることが「難しい」と言うのはなぜですか?

次のようなテストラッパーの何が問題になっています:

 #!/bin/bash
 set -e
 errors=0
 results=$($script_under_test $args<<ENDTSTDATA
 # inputs
 # go
 # here
 #
 ENDTSTDATA
 )
 [ "$?" -ne 0 ] || {
     echo "Test returned error code $?" 2>&1
     let errors+=1
     }

 echo "$results" | grep -q $expected1 || {
      echo "Test Failed.  Expected $expected1"
      let errors+=1
 }
 # and so on, et cetera, ad infinitum, ad nauseum
 [ "$errors" -gt 0 ] && {
      echo "There were $errors errors found"
      exit 1
 }

4
まず、bashスクリプトはあまり読みにくいです。次に、ロックファイルが作成されたbashスクリプトのPIDでロックファイルが作成されているかどうかを確認するなど、予想は複雑です。
nimcap 2009

10
さらに重要なことに、シェルスクリプトは一般に多数の副作用があり、ファイルシステムやネットワークなどのシステムリソースを利用するため、テストが困難です。理想的には、単体テストは副作用がなく、システムリソースに依存しません。
jayhendren 2014年

4

使いやすく便利なツールが欲しかったので、shellspecを作成しました。

純粋なPOSIXシェルスクリプトで記述されています。shunit2より多くのシェルでテストされています。bats / bats-coreより強力な機能があります。

たとえば、ネストされたブロック、モック/スタブが簡単、スキップ/保留が簡単、パラメーター化されたテスト、アサーション行番号、行番号ごとの実行、並列実行、ランダム実行、TAP / JUnitフォーマッター、カバレッジとCI統合、プロファイラーなどをサポートします。 。

プロジェクトページのデモを参照してください。


3

私はshell2junitがかなり好きです、BashスクリプトテストからJUnitのような出力を生成するユーティリティである。生成されたレポートは、JenkinsやBamboo用のJUnitプラグインなどの継続的インテグレーションシステムで読み取ることができるため、これは便利です。

shell2junitのような包括的なバッシュスクリプトフレームワークを提供しませんがshunit2を、それはあなたがテスト結果の素敵な報告を持って認めていません。


3

bashtestを試してください。スクリプトをテストする簡単な方法です。たとえば、do-some-work.shいくつかの設定ファイルを変更したとします。たとえば、PASSWORD = 'XXXXX'設定ファイルに新しい行を追加します/etc/my.cfg

bashコマンドを1行ずつ記述してから、出力を確認します。

インストール:

pip3 install bashtest

テストの作成は、bashコマンドを記述するだけです。

ファイルtest-do-some-work.bashtest

# run the script  
$ ./do-some-work.sh > /dev/null

# testing that the line "PASSWORD = 'XXXXX'" is in the file /etc/my.cfg   
$ grep -Fxq "PASSWORD = 'XXXXX'" /etc/my.cfg && echo "YES"
YES

テストを実行します。

bashtest *.bashtest

あなたは見つけることができ、ここでいくつかの例をしてここに


3

多分これは使用できるか、または貢献することができます

https://thorsteinssonh.github.io/bash_test_tools/

TAPプロトコルで結果を書き込むことを目的としており、CIに適していること、およびシェル環境が必要なユーザーに適していることを想像してください。シェル環境で実行されるものがあると思いますので、シェル環境でテストする必要があると主張する人もいます。


3

assert.shを試してみてください

source "./assert.sh"

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

それが役に立てば幸い!


3

OSHTについて誰も話さなかったなんて信じられない!それは両方と互換性がありますTAPとJUnitのと純粋なシェル(つまり、他の言語は関与していません)であり、スタンドアロンでも動作し、シンプルで直接的です。

テストは次のようになります(プロジェクトページから抜粋したスニペット):

#!/bin/bash
. osht.sh

# Optionally, indicate number of tests to safeguard against abnormal exits
PLAN 13

# Comparing stuff
IS $(whoami) != root
var="foobar"
IS "$var" =~ foo
ISNT "$var" == foo

# test(1)-based tests
OK -f /etc/passwd
NOK -w /etc/passwd

# Running stuff
# Check exit code
RUNS true
NRUNS false

# Check stdio/stdout/stderr
RUNS echo -e 'foo\nbar\nbaz'
GREP bar
OGREP bar
NEGREP . # verify empty

# diff output
DIFF <<EOF
foo
bar
baz
EOF

# TODO and SKIP
TODO RUNS false
SKIP test $(uname -s) == Darwin

簡単な実行:

$ bash test.sh
1..13
ok 1 - IS $(whoami) != root
ok 2 - IS "$var" =~ foo
ok 3 - ISNT "$var" == foo
ok 4 - OK -f /etc/passwd
ok 5 - NOK -w /etc/passwd
ok 6 - RUNS true
ok 7 - NRUNS false
ok 8 - RUNS echo -e 'foo\nbar\nbaz'
ok 9 - GREP bar
ok 10 - OGREP bar
ok 11 - NEGREP . # verify empty
ok 12 - DIFF <<EOF
not ok 13 - TODO RUNS false # TODO Test Know to fail

最後のテストでは「問題あり」と表示されていますが、終了コードはであるため0 TODOです。冗長を設定することもできます:

$ OSHT_VERBOSE=1 bash test.sh # Or -v
1..13
# dcsobral \!= root
ok 1 - IS $(whoami) != root
# foobar =\~ foo
ok 2 - IS "$var" =~ foo
# \! foobar == foo
ok 3 - ISNT "$var" == foo
# test -f /etc/passwd
ok 4 - OK -f /etc/passwd
# test \! -w /etc/passwd
ok 5 - NOK -w /etc/passwd
# RUNNING: true
# STATUS: 0
# STDIO <<EOM
# EOM
ok 6 - RUNS true
# RUNNING: false
# STATUS: 1
# STDIO <<EOM
# EOM
ok 7 - NRUNS false
# RUNNING: echo -e foo\\nbar\\nbaz
# STATUS: 0
# STDIO <<EOM
# foo
# bar
# baz
# EOM
ok 8 - RUNS echo -e 'foo\nbar\nbaz'
# grep -q bar
ok 9 - GREP bar
# grep -q bar
ok 10 - OGREP bar
# \! grep -q .
ok 11 - NEGREP . # verify empty
ok 12 - DIFF <<EOF
# RUNNING: false
# STATUS: 1
# STDIO <<EOM
# EOM
not ok 13 - TODO RUNS false # TODO Test Know to fail

.t拡張機能を使用するように名前を変更してtサブディレクトリに配置すると、prove(1)(Perlの一部)を使用して実行できます。

$ prove
t/test.t .. ok
All tests successful.
Files=1, Tests=13,  0 wallclock secs ( 0.03 usr  0.01 sys +  0.11 cusr  0.16 csys =  0.31 CPU)
Result: PASS

JUnit出力を生成するために設定OSHT_JUNITまたは渡し-jます。JUnitはと組み合わせることもできprove(1)ます。

私は自分のファイルを調達し、その後でアサーションを実行することで機能をテストし、両方のこのライブラリを使用しているIS/ OK利用して、そのネガ、およびスクリプトRUN/をNRUN。私にとって、このフレームワークは、最小限のオーバーヘッドで最大の利益をもたらします。


1

ここで紹介するソリューションの多くを試してみましたが、それらのほとんどがかさばって使いにくいことがわかったので、独自の小さなテストフレームワークを構築しました:https : //github.com/meonlol/t-bash

これは、JUnitスタイルアサートの基本セットを使用して、直接直接実行できるリポジトリ内の1つのファイルにすぎません。

私はいくつかの社内プロジェクトで専門的に使用しており、bashスクリプトを非常に安定して回帰に耐えることができました。



0

Outthenticを見てください。コマンドラインアプリケーションをテストするために、シンプルで多くの言語(Perl、Python、Ruby、Bashを選択可能)およびクロスプラットフォーム(Linux、Windows)フレームワークで拡張できます。


-2

Pythonに次のような大きな利点がある場合、大きなスクリプトにbashを使用することを正当化するのは難しいと思いました。

  • Try / Exceptを使用すると、エラーが発生した場合に変更を元に戻すことができる、より堅牢なスクリプトを作成できます。
  • 'などのあいまいな構文を使用する必要はありませんif [ x"$foo" = x"$bar"]; then ...エラーが発生しやすい '。
  • を使用したオプションと引数の簡単な解析 getoptモジュールます(解析するためのさらに簡単なモジュールがありますが、名前が私をエスケープします)。
  • Pythonでは、基本的な文字列や配列の代わりに、リスト/辞書やオブジェクトを操作できます。
  • 正規表現、データベースなどの適切な言語ツールへのアクセス(すべてをmysqlbash のコマンドにパイプできることを確認してください。ただし、コードを記述する最も良い方法ではありません)。
  • 正しいフォームを使用して心配する必要はありません$*か、"$*"または"$@"または$1または"$1"、ファイル名にスペースの問題ではない、など、など、など

現在、私は最も単純なスクリプトにのみbashを使用しています。


3
Pythonには利点があるという事実を否定するわけではありませんが、2つ目のポイントはあまりよく表現されていません。同じ比較をとして行うこともできますif [[ $foo = $bar ]]; then ...。これはpythonが提供するものよりはまだ良くありませんが、あなたが提示したものよりは良いです。
Shrikant Sharat、2011

8
一部のシステム(例:のために組み込まれている)にはpythonがなく、追加のものをインストールすることはできません。
Rui Marques

2
私は個人的にbashが大好きですが、それが少し証言になる可能性があることに同意します。Pythonでは、エラーが発生したに対処できるのに対し、通常はもっと積極的にする必要があります。ただし、bashにはtrap正規表現(つまり[[ $1 =~ ^[1-3]{3}$ ]])だけでなく(エラーが発生した場合のクリーンアップ/取り消しも)必要があります。あなたが使用したあいまいな構文はtest、bashではなくの古い実装を参照していると確信しています。Bashは既存のコマンドラインツールとのインターフェースに最適です...多くの場合、単一のパイプを使用するawkgrep、Pythonの代替ツールよりもはるかに簡単です。

1
ちなみに、あなたが参照していたパーサーモジュールは、おそらく、optparseまたはその後継argparseです。誰もgetoptモジュールを使用したことはありませんし、個人的に使用したこともありません。getoptしかし、ユーティリティは素晴らしいです。素敵なパターンを見つけたら、シェルからの引数の解析はまったく問題になりません。あなたがgitスタイルのサブコマンドか何かを実装しようとしているのでない限り、それは多くの問題ではありません。

Pythonは、bashが到達できるすべての場所で実行されるわけではありません。私はbashとpythonをテストしたので、同じコードロジックを実行し、両方に何かを要求しました。Bashはアクセスできるすべてのディレクトリに入りました。一方、Pythonは一部のディレクトリのアクセス許可とファイル、および非常に高速で増減するディレクトリを処理できませんでした。
vianna77 2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.