ShellSpec - BDD style testing framework の紹介
まだ shUnit2 や Bats (or Bats-core) で消耗してるの?なんてタイトルで釣ろうかと思いましたが、おとなしめのタイトルにしました。(笑)
前回は ShellSpec の使い方は README.md にまかせて開発したときの思いの丈(?)を書いたのですが、先日 ShellSpec をアップデートし大きく機能強化したのを機に、今回は使い方と新機能を紹介したいと思います。
(2019/08/23追記) 0.20.0 までの機能を追記しました。
(2020/09/17追記) この記事を補完・更新する記事として以下の記事を書きました 。
ShellSpec - シェルスクリプト用のフル機能のBDDユニットテストフレームワーク
ShellSpec とは
ShellSpec は BDD スタイルのテスティングフレームワークです。他の BDD スタイルのテスティングフレームワークと同様に、自然言語に近い DSL で構造化されたテスト(実行可能な例)を記述することができます。
- 公式ウェブサイト https://shellspec.info
- github プロジェクトページ https://github.com/ko1nksm/shellspec
POSIX 互換のシェルスクリプトで実装されており、依存している外部コマンドも POSIX 準拠の基本的なものだけなので bash だけでなく dash や zsh や ksh などより多くのシェルで動作します。OS も様々なものに対応しており、Debian, macOS, Soralis, Windows(WSL)や Docker 上の Alpine Linux や組み込みで使われることが多い BusyBox でもそのまま動きます。
RSpec に大きく影響を受けており、似ている機能や設計でありながら、シェルスクリプトの用途に適した DSL となっています。
スペックファイルについて
以下は ShellSpec の最小のスペックファイルの例です。
Example
The value 123 should equal 123
End
「123 は 123である」というあまり意味がない例ですが、シェルスクリプトを知らなくとも自然言語(英語ですが・・・)として読むことが出来ます。
もう少し意味があるスペックファイルの例です。
# 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
calc 関数が計算式を計算できることを確認する例です。ネスト可能な Describe
でテストを構造的にグループ化し、 It
でテストの説明を行い、 When
~ で関数を呼び出し、The
~ でその出力をチェックしています。
最初の例では Example
を使用しましたが、今回は Example
のエイリアスである It
を使用し、It calculates the formula と読めるようにすることで可読性を上げています。
ちなみに旧バージョンでは It
は Example
のエイリアスではなかったのですが、ShellSpec 自身のスペックファイルを読みやすく書き直しているうちに、やはりこの方法が読みやすいと思い直し、他のツールと同様の方法に変更しました。
今回はスペックファイル自体に calc 関数を定義していますが、もちろん外部ファイルに記述して Include
で読み込むことも可能です。通常はその方法が主な使い方となるでしょう。スペックファイルに直接書いてよいのは、DSL と関数定義のみです。もちろん関数の中は自由に書いてよいですが、関数の外に実行コードは書かないようにしてください。(動作に支障があるということではなくテストの独立性を保つための方針です)
1行目の #shellcheck shell=sh
は ShellSpec ではなく ShellCheck という lint ツールの命令で、このファイルを(bash等ではなく)sh として解釈させるためのものです。すなわちそれは、このスペックファイルがシェルスクリプトの文法として解釈できるということを意味しています。
大文字で始まる DSL(Describe
や It
や End
のことです)があったりインデントでブロックが表現されていたりしますが、それでもスペックファイルはシェルスクリプトの文法として正しいものとなります。そのためスペックファイルに対して shellcheck
や sh -n
を使用した構文チェックなど既存のツールを使用することが出来ます。
しかしながらスペックファイルが直接シェル上で動くわけではありません。ShellSpec が実行時に動的にコードを変換しています。DSL は ShellSpec の内部関数の呼び出しに変換され、ブロックはサブシェルに変換されます。(変換が行われるのはスペックファイルのみです。外部スクリプトは変換されません)
ブロックをサブシェルに変換することで各テストはそれぞれ独立した環境で実行されます。これによりレキシカルスコープに似たスコープを実現しています。各テストをサブシェルで実行することでテストの独立性を保ち、ローカル変数や関数の一時的な再定義(モック・スタブ)も行えるようになります。(ちなみにシェルスクリプトの local
や typeset
は、POSIX準拠ではなくダイナミックスコープなので使用していません。)
レポート機能
最近のテストツール同様、モダンな表示を行います。ShellSpec を実行すると、成功したテストと失敗したテストがそれぞれ表示され、失敗したテストは行番号とともに失敗した理由が表示されます。(shUnit2 では行番号の表示に ${_ASSERT_EQUALS_}
を使用した特別な書き方と、$LINENO
変数に対応したシェルが必要ですが、ShellSpec はすべてのシェルで行番号の表示が行なえます!Debian や Ubuntu の dash は パフォーマンスを理由に $LINENO
変数は無効にされているのです・・・)
複数の出力形式に対応しており、デフォルトのドットによる表示 (progress) の他、ドキュメントとして読みやすい documentation や tap 形式に対応しています。
progress formatter (default)
documentation formatter
tap formatter
junit formatter
(省略)
フィルタリング (2019/08/23追記)
テスト実行の際に、行番号、ID、フォーカス、テスト名、タグ、といった様々な方法で実行するテストを指定することが出来ます。一番使用頻度が多いと思われる行番号は、(RSpecのように)以下の方法で指定することが出来ます。
shellspec path/to/a_spec.sh:10:20 # 10行目と20行目を含むテストを実行
またテストの、Descript
や It
をそれぞれ fDescribe
、fIt
と書き換えることでフォーカスを指定し、フォーカスされたテストだけを実行することが出来ます。これも RSpec を参考に実装したのですが、RSpecとは違い実行するときに --focus
オプションが必要です。(指定しない場合はすべて実行されます)
これは ShellSpec の設計上 1 パス、つまりスペックファイルを変換しながらそのまま実行しているため、最初にスペックファイルにフォーカスされた行があるか知ることが不可能だからです。--focus
オプションが必要なのは制限とも思えるのですが実際のテスト修正では、エラーとなった行を修正 → フォーカスしたものだけを実行 → 全体を実行して確認 → エラーが修正されていなければその箇所のみ修正 → 繰り返す と フォーカスしたものと全体の実行を交互に行うと思うので、これはこれで良かったかなぁと思っています。
並列実行 (2019/08/23追記)
バージョン 0.10.0 で並列実行に対応しました。並列実行は Bats-core でも対応予定(おそらく1.2.0から。master には含まれています)ですが、 Bats-core では GNU parallel が必要なのに対して ShellSpec はシェルスクリプトのバックグラウンドプロセスを利用しシェルスクリプトのみで実装しています。そのためすべてのシェルで利用することが出来ます。
ランダム実行 (2019/08/23追記)
バージョン 0.14.0 でスペックファイルのランダム実行に対応しました。これもシェルスクリプト(とsort
とod
コマンド)のみで実行しています。実行順のランダム化には fnv1a
で(od
コマンドで 8 進数に変換した)ファイル名をハッシュ化し xorshift32
でハッシュ値からランダム値を求めてsortしています。
ハッシュを求めるコマンドは cksum
(や sha1sum
、md5sum
等)を使用することも出来たのですが、これだとファイルの数だけコマンド呼び出しが発生するので遅くなります。また $RANDOM
や /dev/urandom
でランダム値を取得することも可能ですが、シェルや OS に依存します。そのためハッシュ化とランダム値の計算をシェルスクリプトで実装しました。どちらも軽量なアルゴリズムのためシェルスクリプトで実装しても特に遅さは感じません。
カバレッジ統合 (2019/08/23追記)
バージョン 0.16.0 でkcov を統合させることにより、カバレッジ機能に対応させました。もちろんカバレッジレポートも出力できるのでテストを行っていない箇所を可視化することが出来ます。
どのテスティングフレームワークにも言えることですがカバレッジ機能がフレームワークに統合されていない場合、それを自分で統合させるのはけっこう大変な作業です。ShellSpec では設計上「Kcov 上で ShellSpecを実行」するだけではカバレッジを取ることが出来ないため、ShellSpec に統合させました。
なおカバレッジを取ることが出来るシェルは bash のみです。(Kcov は bash のみに対応しており、Kcov は bash のデバッグ機能に依存しています。)これが今の所唯一の特定のシェルでしか使えない機能となります。 bash, zsh, ksh です。Kcov は bash しか対応していませんが、bash と同様の出力を行うことで、zsh と ksh に対応させました。(2020/05/11)
プロファイラ対応 (2019/08/23追記)
バージョン 0.18.0 でプロファイラに対応しています。時間がかかっているテストをリストアップすることができます。が、はっきり言ってこれは一番意味がない機能だと思っています(笑)
アプリケーションのプロファイラならともかく、テストのプロファイラにどういう意味があるのでしょうか?時間がかかっているテストだから短くしようという意味がなくはないですが、テストで時間がかかっているからと言って、実際のアプリケーションではボトルネックになっていなかったり、逆にテストでは短い時間でも実際のアプリケーションでは何度も呼び出すためボトルネックになるということがあるのでテストでプロファイラを使用してもあまり意味がないと思っています。
ではなぜ実装したのか?ですが、jUnit XML で(必須ではないですが)テスト実行時間の項目があるからです。というのは建前で、面白い実装方法を思いつたからです。
多くの環境をサポートしようとするとシェルスクリプトでミリ秒の取得ができないという制限にぶち当たります。date
コマンドは POSIX の範囲ではミリ秒の取得ができません。POSIX の範囲に限定するとシェルスクリプトでミリ秒を取得できるコマンドは time
と times
のみです。このうち times
は user 時間と sys 時間は取得できますが肝心の real 時間が取得できません。そのため使えるコマンドは time
のみです。しかし time
は呼び出したコマンドの実行時間を調べるものなので、テスト一つごとに外部プロセスとして呼び出さなければいけません。テストでシェル関数呼び出しなどを行うのでそれは不可能です。
そこでとった手段が、プロファイリングのために数値をカウントするだけのプロセスをバックグラウンド実行させるという方法です。テスト全体の実行時間は time
でわかるので、あとはテスト 1 件の開始と終了のタイミング(=カウント)がわかれば計算できます。テストの開始と終了の時点で、プロファイラプロセスに通知してその時のカウント値を記録させています。(最初はシグナルを用いて通知していましたが扱いが大変だったのでファイルを使用しています。)
つまり CPU コア一つを全力でぶん回すというかなり強引な手段です。テストの実行時間に影響があるのでは?とか精度が悪いのでは?という懸念はありましたがそれなりに上手く言っているようです。(あまり意味がない機能だし・・・)
シングルスクリプトファイルテスト (2019/08/23追記)
バージョン 0.19.0でシングルスクリプトファイルのテストに対応しました。多くのシェルスクリプトは1ファイルの実行可能なスクリプトとして作成されていることが多いと思います。関数だけで定義されているライブラリファイルを読み込む場合は ShellSpec の Include
で簡単に読み込んでテストできるのですが、実行可能なスクリプトファイルになっている場合テストが困難でした。
バージョン 0.19.0で対応した機能により、シェルスクリプトに Interception point
を入れておくことでその場所で処理を割り込ませたり、特定の行でスクリプトを中断させる(Sourced Return
)ことで、実行可能なスクリプトファイルのテストを可能にしました。
パラメーター化テスト (2019/08/23追記)
バージョン 0.20.0 でパラメータ化テストに対応しました。jUnit5 や rspec-parameterized にあるような機能でテストに対してパラメータを定義することで同様のテストをパラメータを変えて実行する機能です。
実は当初の脳内設計には含まれていなくて、あとから追加したものなのですが、既存のテストのパラメータを定義するだけで簡単にパラメータ化テストに変更できるというシンプルな使い勝手を実現できたのでかなり気に入っています。(DSL はページの下の方を参照)
使い方
インストール
github からコードを clone し、PATHが通った場所にシンボリックリンクを作成するだけです。
$ cd /SOME/WHERE/TO/INSTALL
$ git clone https://github.com/ko1nksm/shellspec.git
$ ln -s /SOME/WHERE/TO/INSTALL/shellspec/shellspec /EXECUTABLE/PATH/
# (e.g. /EXECUTABLE/PATH/ = /usr/local/bin/, $HOME/bin/)
もしくは tar.gz をダウンロードして展開しても良いです。
$ cd /SOME/WHERE/TO/INSTALL
$ wget https://github.com/ko1nksm/shellspec/archive/0.8.0.tar.gz
$ tar xzvf shellspec-0.8.0.tar.gz
$ ln -s /SOME/WHERE/TO/INSTALL/shellspec-0.8.0/shellspec /EXECUTABLE/PATH/
# (e.g. /EXECUTABLE/PATH/ = /usr/local/bin/, $HOME/bin/)
はじめの一歩
通常はテスト対象のプロジェクトディレクトリがあると思うので、そのディレクトリ(なければ新規に作成してください)で shellspec --init
を実行してください。スペックファイルを格納する spec
ディレクトリと設定用のファイルが作成されます。
$ cd /YOUR/PROJECT/DIRECTORY
$ shellspec --init
create .shellspec
create spec/spec_helper.sh
(とは言ってもこれらのファイルは必須ではなく、適当な場所にスペックファイルを作成して、shellspec
に渡せば実行できるのですが・・・)
あとは、spec
ディレクトリの中にスペックファイルを作成していき、プロジェクトディレクトリ直下で shellspec
を実行すれば、スペックファイルが実行されます。(スペックファイルの名前の末尾は _spec.sh
である必要があります。)
DSL
ShellSpec の DSL は大きく、Example group、Example、Evaluation、Expectation に分かれています。これらにあてはまらないものとして Helper と Directive があります。
Describe 'calc()' # Example group
calc() { echo "$(($*))"; }
It 'calculates the formula' # Example
When call calc 1 + 2 # Evaluation
The output should equal 3 # Expectation
End
End
Example group
BDD では一つのテストのことを実行可能な例(Example) と呼び、ShellSpec もそれに倣っています。
Example group は Example をグループ化するためのブロック構文です。ブロック構文とは特定のキーワードで始まり
End
で終わる構文です。これは ShellSpec DSL の拡張構文で純粋なシェルスクリプトの構文ではありません。(シェルスクリプトとしてみると開始と終わりはそれぞれただの 1 命令として扱われます。)
Example group のDSLには Describe
と Context
があり、機能的にはどちらも同じですが、テスト対象についての説明を行う時に Describe
を使用し、テストの状況を説明するときには Context
を使用します。これらはネスト可能で、テスト対象や状況に応じて構造化してグループ化することが出来ます。
Example
実行可能な例 (Example) です。このブロックの中に、何をして (Evaluation) どうあるべきか?(Expectation) を記述します。
Example group の DSL には ブロック構文の Example
、Specify
、It
、ワンライナー構文の Todo
があります。It
を使って書くと説明をより自然な文章にすることが出来ますが、しっくりこない場合には、同等の機能である Example
や Specify
を使用することができます。Todo
は実装予定の例を一行で記述するための DSLで、内容がない Example
と同等の意味になります。
Evaluation
Example の中の When
で始まる行が Evaluation です。Evaluation でテスト対象のシェル関数や外部コマンドを呼び出します。Evaluation は一つの Example の中に一つしか書けません。(省略することは出来ます。)
When call calc 1 + 2
When
の次の call
はシェル関数もしくは外部コマンドを呼び出すことを意味し、他に外部コマンドのみを呼び出す run
やサブシェル内で呼び出す invoke
(特殊な用途用なのであまり使いません)があります。一般的には call
を使用すれば十分でしょう。
Expectation
Example の中の The
で始まる行が Expectation です。Evaluation の実行結果を検証します。Expectation は複数書くことが出来ます。複数書いた場合でも途中の失敗で停止することなくすべての Expectation が実行されます。(RSpec の aggregate_failures
相当の動作です。)
Expectation は以下のような構成になっています。
The output should equal 3
| | |
| | +-- equal が matcher です。この場合は3であることを検証します。
| +-- output が subject (検証対象) です。この場合は標準出力を検証します。
+-- Expectation は The で始まります。
should
の代わりに should not
を使用することで、否定の意味にすることも出来ます。
標準出力の2行目を検証したい等といった場合に使用するのが modifier です。 modifier は subject の内容を加工し新たな subject とします。
The line 2 of output should equal 3
|
+-- line が modifier です。この場合は標準出力の2行目を新たな subject とします。
modifier はつなげて書くことも出来ます。また数値は序数で書くことも出来ます。
The word 1 of line 2 of output should equal 3
The first word of second line of output should equal 3
subject、modifier、matcher には、標準エラー出力や終了ステータスや検証するための subject やファイルの状態や文字列が特定のパターンにマッチしているかを検証する matcher など、他にも色々あります。詳細はプロジェクトサイトを参照してください。
Helper
Include, Before, After
以下は、Include
、Before
、After
を使用したサンプルです。
(mylib.sh の中にはデータファイルの件数を数える count 関数と合計を求める sum 関数があると考えてください)
Describe "mylib.sh"
Include ./mylib.sh
Before "setup"
After "cleanup"
setup() {
{
echo 10
echo 20
echo 30
} > "$SHELLSPEC_TMPBASE/data.txt"
}
cleanup() {
rm "$SHELLSPEC_TMPBASE/data.txt"
}
Describe 'count()'
It 'counts data file'
When call count "$SHELLSPEC_TMPBASE/data.txt"
The output should equal 3
End
End
Describe 'sum()'
It 'sums data file'
When call sum "$SHELLSPEC_TMPBASE/data.txt"
The output should equal 60
End
End
End
Before
、After
はそれぞれ、各 Example 実行の前後に呼び出されるフック関数(またはコード)を指定できます。初期化処理や終了処理を書く時に使用します。
Before
、After
で指定した関数が実行されるのは Example 毎であることに注意してください。つまりこの例の場合、setup
関数と cleanup
関数はそれぞれ2回ずつ呼び出されます。
Include
も内部的にBefore
相当のコードになるため、Example 毎に読み込まれます。(2019/03/28 追記 関数の再定義を直感的に記述できないため 0.9.0 でその場で読み込むように仕様変更します、)
スペック全体や Describe
単位で呼び出されるいわゆる BeforeAll
や BeforeContext
に相当する機能は意図的に実装していません。テストの独立性を壊す可能性があるためです。(とはいえ、スペックファイルはシェルスクリプトとして実行されるので実行したい箇所にコードを書けば普通に動くわけですが・・・。推奨はしません。)
Skip, Skip if, Pending
Skip
を使用することで、現在のブロックの Skip
以降の処理をスキップすることが出来ます。Skip if
は条件付きスキップで特定の環境でのみ Example をスキップしたいというときに使用します。
また Describe
、Context
、Example
、Specify
、It
のそれぞれの頭にx
をつけてxDescribe
、xContext
、xExample
、xSpecify
、xIt
とすることでブロック自体を簡単にスキップすることもできます。
Pending
は Skip
と似ていますが機能が未実装であることを示す時に使用します。Pending
は Example の実行を単にスキップする Skip
とは違い Example を実行します。そして検証結果が失敗(未実装なので想定通り)であれば、スペック自体は成功となり、実装が完了し検証結果が成功となれば、スペックは失敗となり Pending
を取り除くべきということがわかります。
Data
ShellSpec 0.8.0 からの新機能です。シェルスクリプトではしばしばデータの入力を標準入力から受け取るのですがそれを簡潔に書くことができる機能です。Data
を使用すると以下のように標準入力からのデータ入力を記述することが出来ます。
Describe "sort command"
Data
#|2
#|3
#|1
End
It 'sorts data'
When call sort
The line 1 of output should equal 1
The line 2 of output should equal 2
The line 3 of output should equal 3
End
End
Data
ブロックの中のコメントの行頭から #|
を除いた残りの部分が標準入力データになります。Data:expand
とすることで変数展開を行ったり、関数の出力結果を標準入力データとしたり Data | <FILTER>
と書くことで別のフィルタを間に入れるといったことも可能です。
Directive
これも ShellSpec 0.8.0 からの機能です。他の DSL とは少し違う扱いなので Directive という別の名前で呼んでいます。
これらを使用して先程の Before, After のサンプルコードを書き直すと以下のようになります。
% DATA_FILE: "$SHELLSPEC_TMPBASE/data.txt"
Describe "mylib.sh"
Include ./mylib.sh
Before "setup"
After "cleanup"
setup() {
%text > "$DATA_FILE"
#|10
#|20
#|30
}
cleanup() { rm "$DATA_FILE"; }
Describe 'count()'
It 'counts data file'
When call count "$DATA_FILE"
The output should equal 3
End
End
Describe 'sum()'
It 'sums data file'
When call sum "$DATA_FILE"
The output should equal 60
End
End
End
%
は %const
の別名で定数を定義します。ただしこの定数の値はスペックファイルの変換時に決定されます。つまり ShellSpec の環境変数である $SHELLSPEC_TMPBASE
などを参照することは出来ますが、スペックファイルの変数や関数は参照できません。
もともとこの機能は、条件付きスキップで必要になりました。通常は Example を実行の起点とし、Before
等で変数の初期化などを行うのですが、 Skip if
は、Example 外で処理が実行されるため、まだ変数が初期化されていないわけです。そのため内部で作成する変数ではなく外部から与えられる定数という扱いで、Describe
の外でファイルレベルの定数を定義できるようにしました。これは定数であるため名前は大文字限定としています。(これは ShellCheck の警告を抑制する効果もあります。)今はただの変数として実装されているためスペックファイル内で値の変更ができるのですが将来的には readonly にするかもしれません。
そしてもう一つの Directive が 埋め込みテキストである %text
です。この Directive は他の DSL と違って関数の中で使用します。%text
はシェルスクリプトのヒアドキュメントの代わりとなる機能を提供します。先程の setup
関数をヒアドキュメントを使用して書くと以下のようになります。
setup() {
cat < 'HERE' > "$DATA_FILE"
10
20
30
HERE
}
シェルスクリプトのヒアドキュメントはインデントできないため、インデントされたコードで非常に見づらいものとなってしまいます。(正確にはハードタブを使用すればインデントは可能なのですが、ヒアドキュメントの終わりはインデントできず、ぱっと見でタブとスペースの区別がつかないためいろいろと問題があります。)
先程の例では %text
の内容をリダイレクトでファイルに出力しましたが、リダイレクトを書かなければ標準出力に出力されます。Data
ブロックと同様、%text:expand
で変数展開を行ったり別のフィルタと組み合わせることも出来ます。
Data
ブロックの例を %text
を使用すると以下のように書くことが出来ます。
Describe "sort command"
Data
#|2
#|3
#|1
End
expected() {
%text
#|1
#|2
#|3
}
It 'sorts data'
When call sort
The output should equal "$(expected)"
End
End
モック・スタブ
テストではしばしばモックやスタブを使用する必要が出てきます。現在 ShellSpec にはそのための専用の機能はありませんが、関数を一時的に置き換えることで実現できます。
以下は date
コマンドのスタブをシェル関数で作成する例です。
Describe 'mock stub sample'
unixtime() { date +%s; }
get_next_day() { echo $(($(unixtime) + 86400)); }
Example 'redefine date command'
date() { echo 1000000000; }
When call get_next_day
The stdout should eq 1000086400
End
Example 'use the date command'
# date is not redefined because this is another subshell
When call unixtime
The stdout should not eq 1000000000
End
End
get_next_day
関数は内部で date
コマンドを呼び出しています。しかし直接 date
コマンドを呼び出してしまうと実行するたびに値が変わるためテストになりません。そのため date
コマンドを date
関数で上書きしています。これはサブシェル内の一時的な上書きであることに注意してください。そのためサブシェルを抜けると元の date
コマンドが呼び出されるようになります。
この例では実際には外部コマンドを関数で上書きしており、関数を関数で上書きしたわけではありませんが、例えば unixtime 関数をブロック内で再定義してもサブシェルを抜けると元に戻ります。
ブロック構造で上書きの範囲が明確になるため直感的に記述することが出来ます。
パラメーター化テスト (2019/08/23追記)
パラメータを定義することで同様のテストを複数回実行させる機能です。
まず、パラメータ化テストを行わない場合の例です。
Describe 'example'
Example "example #1"
When call echo "$((10 + 20))"
The output should eq "30"
End
Example "example #2"
When call echo "$((100 + 200))"
The output should eq "300"
End
End
これをパラメータ化テストに置き換えると以下のようになります。
Describe 'example'
Parameters
"#1" 10 20 30
"#2" 100 200 300
End
Example "example $1"
When call echo "$(($2 + $3))"
The output should eq "$4"
End
End
Parameters
でパラメータを複数定義し、テストはパラメータの部分を $1
, $2
, ... で置き換えるだけで、簡単にパラメータ化テストに変更することが出来ます。
パラメータの定義方法は現在以下の4つの方法をサポートしています。
# block style (default: same as Parameters)
Parameters:block
"#1" 1 2 3
"#2" 1 2 3
End
# value style
Parameters:value foo bar baz
# matrix style
# 以下のように解釈されます
# foo 1
# foo 2
# bar 1
# bar 2
Parameters:matrix
foo bar
1 2
End
# dynamic style
# コードで動的にパラメータを生成できます。
Parameters:dynamic
for i in 1 2 3; do
%data "#$i" 1 2 3
done
End
最後に
こうしてみると色々と機能があるのですが、スペックファイルの文法はシンプルで自然言語に近いため覚えやすく簡単に使えると思います。その他のサンプル もこちらに用意しています。shUnit2 や Bats は実際に使ってみるとわかると思いますが、テストコードを読むのにシェルスクリプトの知識が必要になります。ShellSpec ではそれが必要最小限に抑えられているためレビューも容易になると思います。
また殆どの処理をシェルの内部コマンドだけで行っており、外部コマンドの呼び出しをほとんど行わないため極めて軽快に動作します。(テスト内容によるためはっきりとは言えませんが、shUnit2 や Bats の2~5倍以上の速度で動作するようです。2019/04/09 追記 0.10.0でサポート予定の並列実行機能でさらに2倍程度高速化します。)高速にテストを実行できることもテストツールの重要な要件の一つです。
シェルスクリプトでテストコードが必要なほど大きなものは作らないという考え方もあるかと思いますがテストは重要です。もし必要になったときは思い出してぜひ使ってみてください。