と書くことで別のフィルタを間に入れるといったことも可能です。, これも ShellSpec 0.8.0 からの機能です。他の DSL とは少し違う扱いなので Directive という別の名前で呼んでいます。, これらを使用して先程の Before, After のサンプルコードを書き直すと以下のようになります。, % は %const の別名で定数を定義します。ただしこの定数の値はスペックファイルの変換時に決定されます。つまり ShellSpec の環境変数である $SHELLSPEC_TMPBASE などを参照することは出来ますが、スペックファイルの変数や関数は参照できません。, もともとこの機能は、条件付きスキップで必要になりました。通常は Example を実行の起点とし、Before 等で変数の初期化などを行うのですが、 Skip if は、Example 外で処理が実行されるため、まだ変数が初期化されていないわけです。そのため内部で作成する変数ではなく外部から与えられる定数という扱いで、Describe の外でファイルレベルの定数を定義できるようにしました。これは定数であるため名前は大文字限定としています。(これは ShellCheck の警告を抑制する効果もあります。)今はただの変数として実装されているためスペックファイル内で値の変更ができるのですが将来的には readonly にするかもしれません。, そしてもう一つの Directive が 埋め込みテキストである %text です。この Directive は他の DSL と違って関数の中で使用します。%text はシェルスクリプトのヒアドキュメントの代わりとなる機能を提供します。先程の setup 関数をヒアドキュメントを使用して書くと以下のようになります。, シェルスクリプトのヒアドキュメントはインデントできないため、インデントされたコードで非常に見づらいものとなってしまいます。(正確にはハードタブを使用すればインデントは可能なのですが、ヒアドキュメントの終わりはインデントできず、ぱっと見でタブとスペースの区別がつかないためいろいろと問題があります。), 先程の例では %text の内容をリダイレクトでファイルに出力しましたが、リダイレクトを書かなければ標準出力に出力されます。Data ブロックと同様、%text:expand で変数展開を行ったり別のフィルタと組み合わせることも出来ます。, Data ブロックの例を %text を使用すると以下のように書くことが出来ます。, テストではしばしばモックやスタブを使用する必要が出てきます。現在 ShellSpec にはそのための専用の機能はありませんが、関数を一時的に置き換えることで実現できます。, get_next_day 関数は内部で date コマンドを呼び出しています。しかし直接 date コマンドを呼び出してしまうと実行するたびに値が変わるためテストになりません。そのため date コマンドを date 関数で上書きしています。これはサブシェル内の一時的な上書きであることに注意してください。そのためサブシェルを抜けると元の date コマンドが呼び出されるようになります。, この例では実際には外部コマンドを関数で上書きしており、関数を関数で上書きしたわけではありませんが、例えば unixtime 関数をブロック内で再定義してもサブシェルを抜けると元に戻ります。, Parametersでパラメータを複数定義し、テストはパラメータの部分を $1, $2, ... で置き換えるだけで、簡単にパラメータ化テストに変更することが出来ます。, こうしてみると色々と機能があるのですが、スペックファイルの文法はシンプルで自然言語に近いため覚えやすく簡単に使えると思います。その他のサンプル もこちらに用意しています。shUnit2 や Bats は実際に使ってみるとわかると思いますが、テストコードを読むのにシェルスクリプトの知識が必要になります。ShellSpec ではそれが必要最小限に抑えられているためレビューも容易になると思います。, また殆どの処理をシェルの内部コマンドだけで行っており、外部コマンドの呼び出しをほとんど行わないため極めて軽快に動作します。(テスト内容によるためはっきりとは言えませんが、shUnit2 や Bats の2~5倍以上の速度で動作するようです。2019/04/09 追記 0.10.0でサポート予定の並列実行機能でさらに2倍程度高速化します。)高速にテストを実行できることもテストツールの重要な要件の一つです。, シェルスクリプトでテストコードが必要なほど大きなものは作らないという考え方もあるかと思いますがテストは重要です。もし必要になったときは思い出してぜひ使ってみてください。, おそらくウェブアプリエンジニア。フロントやったりサーバーやったりたまにインフラ。好きなもの:シンプルで無駄のないコード、リファクタリング。嫌いなもの:技術的負債、レガシーコード. script.sh # Unit tests shUnitでテストしたいシェルスクリプトがあります。 スクリプト(とすべての関数)は、インストールがはるかに簡単になるため、単一のファイルにあります。 ASSERT:【文字が引数】Nullでない, シェルスクリプトは性質上、上から下に処理が流れてしまうため、shUnit2を使用する場合は以下の構成にするのがよいと思います。, shUnit2はブラックボックステストになるので、ブラックボックスをなるべく小さくして、それぞれを検証していくスタイルのほうがテストの堅牢性を守りつつ、ミスの少なスクリプトが作れると思います。, BASH Debugger flk_test.func に定義されている関数は3つ。, ここでそれぞれの関数の依存関係を考慮します。依存性の小さい関数からテストをすることで、依存されている側(flt_testに於いては fla_test_main)の確認観点を絞ることができるためです。, 関数名を確認観点にすると、実際にテストを実行した時の見た目で何を確認したのが分かるようになります。, assertEquals と assertNull はshUnit2で用意されているアサーション関数です。こちらは他にもいろいろと関数があるのでマニュアルを参照してもらえればと思いますが、概ねJUnitと同じ考えです。, <<アサーション失敗時のメッセージ>>はアサーションが失敗した時に標準出力に出力されるので、被疑箇所の特定がしやすくなります。, ASSERT:【文字が引数】Nullでない シェルスクリプトのユニットテスト (5) . 例えば下記のようなコードのシェルを実行したときにrmコマンドを意図的に失敗させて「失敗」を表示させるにはどうすればよいでしょうか?rmコマンドは例えなので、rmコマンドに限らず他のコマンドでも共通で意図的に失敗させることができるような方法を教えてほしいです。 #!/bin/csh -frm /et 「ごりごりの複雑なロジックだから何かが起きそう」, そうすると機能テストをする流れになると思いますが、ざっと確認してみたところ、以下の方法が考えられそう。, 今回はタイトルの通り、shUnit2について取り上げたいと思います。Batsは気になるから別の機会に。, shUnit2は前項でも書いたとおり、シェルスクリプト向け自動テストフレームワークです。 shUnit2 2.1.x nドキュメント これらの組み合わせ 16 パターンについて、テストを実行するシェルスクリプトを作成します。 Param1 が D で、Param2 が c のときの処理にバグがあって、テストが失敗するものとします。 単純に for を2重にループさせ、テストプログラムを実行することにします。 Endで終わる構文です。これは ShellSpec DSL の拡張構文で純粋なシェルスクリプトの構文ではありません。(シェルスクリプトとしてみると開始と終わりはそれぞれただの 1 命令として扱われます。), Example group のDSLには Describe と Context があり、機能的にはどちらも同じですが、テスト対象についての説明を行う時に Describe を使用し、テストの状況を説明するときには Context を使用します。これらはネスト可能で、テスト対象や状況に応じて構造化してグループ化することが出来ます。, 実行可能な例 (Example) です。このブロックの中に、何をして (Evaluation) どうあるべきか?(Expectation) を記述します。, Example group の DSL には ブロック構文の Example、Specify、It、ワンライナー構文の Todo があります。It を使って書くと説明をより自然な文章にすることが出来ますが、しっくりこない場合には、同等の機能である Example や Specify を使用することができます。Todo は実装予定の例を一行で記述するための DSLで、内容がない Example と同等の意味になります。, Example の中の When で始まる行が Evaluation です。Evaluation でテスト対象のシェル関数や外部コマンドを呼び出します。Evaluation は一つの Example の中に一つしか書けません。(省略することは出来ます。), When の次の call はシェル関数もしくは外部コマンドを呼び出すことを意味し、他に外部コマンドのみを呼び出す run やサブシェル内で呼び出す invoke (特殊な用途用なのであまり使いません)があります。一般的には call を使用すれば十分でしょう。, Example の中の The で始まる行が Expectation です。Evaluation の実行結果を検証します。Expectation は複数書くことが出来ます。複数書いた場合でも途中の失敗で停止することなくすべての Expectation が実行されます。(RSpec の aggregate_failures 相当の動作です。), should の代わりに should not を使用することで、否定の意味にすることも出来ます。, 標準出力の2行目を検証したい等といった場合に使用するのが modifier です。 modifier は subject の内容を加工し新たな subject とします。, modifier はつなげて書くことも出来ます。また数値は序数で書くことも出来ます。, subject、modifier、matcher には、標準エラー出力や終了ステータスや検証するための subject やファイルの状態や文字列が特定のパターンにマッチしているかを検証する matcher など、他にも色々あります。詳細はプロジェクトサイトを参照してください。, 以下は、Include、Before、After を使用したサンプルです。 #!/bin/sh echo $1 シェルスクリプトでも単体テストを書いてCIした方が精神衛生上好ましいなと思い、調べてみるとshUnit2という単体テストフレームワークがあったので、これを使って単体テストとCIを実施し … kcov スクリプトの作成し終えたら、単体テストをおこないます。 想定した前提条件で スクリプトを実行して、想定した結果となることを確認する「正常系」のテストと ifやwhileなどで分岐やループをしているすべての箇所が、想定した動作になっていることを確認する「異常系」のテスト … ShellSpec - シェルスクリプト用のフル機能のBDDユニットテストフレームワーク, ShellSpec は BDD スタイルのテスティングフレームワークです。他の BDD スタイルのテスティングフレームワークと同様に、自然言語に近い DSL で構造化されたテスト(実行可能な例)を記述することができます。, POSIX 互換のシェルスクリプトで実装されており、依存している外部コマンドも POSIX 準拠の基本的なものだけなので bash だけでなく dash や zsh や ksh などより多くのシェルで動作します。OS も様々なものに対応しており、Debian, macOS, Soralis, Windows(WSL)や Docker 上の Alpine Linux や組み込みで使われることが多い BusyBox でもそのまま動きます。, RSpec に大きく影響を受けており、似ている機能や設計でありながら、シェルスクリプトの用途に適した DSL となっています。, 「123 は 123である」というあまり意味がない例ですが、シェルスクリプトを知らなくとも自然言語(英語ですが・・・)として読むことが出来ます。, 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 形式に対応しています。, テスト実行の際に、行番号、ID、フォーカス、テスト名、タグ、といった様々な方法で実行するテストを指定することが出来ます。一番使用頻度が多いと思われる行番号は、(RSpecのように)以下の方法で指定することが出来ます。, またテストの、Descript や It をそれぞれ fDescribe、fIt と書き換えることでフォーカスを指定し、フォーカスされたテストだけを実行することが出来ます。これも RSpec を参考に実装したのですが、RSpecとは違い実行するときに --focus オプションが必要です。(指定しない場合はすべて実行されます), これは ShellSpec の設計上 1 パス、つまりスペックファイルを変換しながらそのまま実行しているため、最初にスペックファイルにフォーカスされた行があるか知ることが不可能だからです。--focus オプションが必要なのは制限とも思えるのですが実際のテスト修正では、エラーとなった行を修正 → フォーカスしたものだけを実行 → 全体を実行して確認 → エラーが修正されていなければその箇所のみ修正 → 繰り返す と フォーカスしたものと全体の実行を交互に行うと思うので、これはこれで良かったかなぁと思っています。, バージョン 0.10.0 で並列実行に対応しました。並列実行は Bats-core でも対応予定(おそらく1.2.0から。master には含まれています)ですが、 Bats-core では GNU parallel が必要なのに対して ShellSpec はシェルスクリプトのバックグラウンドプロセスを利用しシェルスクリプトのみで実装しています。そのためすべてのシェルで利用することが出来ます。, バージョン 0.14.0 でスペックファイルのランダム実行に対応しました。これもシェルスクリプト(とsortとodコマンド)のみで実行しています。実行順のランダム化には fnv1a で(od コマンドで 8 進数に変換した)ファイル名をハッシュ化し xorshift32 でハッシュ値からランダム値を求めてsortしています。, ハッシュを求めるコマンドは cksum (や sha1sum、md5sum等)を使用することも出来たのですが、これだとファイルの数だけコマンド呼び出しが発生するので遅くなります。また $RANDOM や /dev/urandom でランダム値を取得することも可能ですが、シェルや OS に依存します。そのためハッシュ化とランダム値の計算をシェルスクリプトで実装しました。どちらも軽量なアルゴリズムのためシェルスクリプトで実装しても特に遅さは感じません。, バージョン 0.16.0 でkcov を統合させることにより、カバレッジ機能に対応させました。もちろんカバレッジレポートも出力できるのでテストを行っていない箇所を可視化することが出来ます。, どのテスティングフレームワークにも言えることですがカバレッジ機能がフレームワークに統合されていない場合、それを自分で統合させるのはけっこう大変な作業です。ShellSpec では設計上「Kcov 上で ShellSpecを実行」するだけではカバレッジを取ることが出来ないため、ShellSpec に統合させました。, なおカバレッジを取ることが出来るシェルは bash のみです。(Kcov は bash のみに対応しており、Kcov は bash のデバッグ機能に依存しています。)これが今の所唯一の特定のシェルでしか使えない機能となります。 bash, zsh, ksh です。Kcov は bash しか対応していませんが、bash と同様の出力を行うことで、zsh と ksh に対応させました。(2020/05/11), バージョン 0.18.0 でプロファイラに対応しています。時間がかかっているテストをリストアップすることができます。が、はっきり言ってこれは一番意味がない機能だと思っています(笑), アプリケーションのプロファイラならともかく、テストのプロファイラにどういう意味があるのでしょうか?時間がかかっているテストだから短くしようという意味がなくはないですが、テストで時間がかかっているからと言って、実際のアプリケーションではボトルネックになっていなかったり、逆にテストでは短い時間でも実際のアプリケーションでは何度も呼び出すためボトルネックになるということがあるのでテストでプロファイラを使用してもあまり意味がないと思っています。, ではなぜ実装したのか?ですが、jUnit XML で(必須ではないですが)テスト実行時間の項目があるからです。というのは建前で、面白い実装方法を思いつたからです。, 多くの環境をサポートしようとするとシェルスクリプトでミリ秒の取得ができないという制限にぶち当たります。date コマンドは POSIX の範囲ではミリ秒の取得ができません。POSIX の範囲に限定するとシェルスクリプトでミリ秒を取得できるコマンドは time と times のみです。このうち times は user 時間と sys 時間は取得できますが肝心の real 時間が取得できません。そのため使えるコマンドは time のみです。しかし time は呼び出したコマンドの実行時間を調べるものなので、テスト一つごとに外部プロセスとして呼び出さなければいけません。テストでシェル関数呼び出しなどを行うのでそれは不可能です。, そこでとった手段が、プロファイリングのために数値をカウントするだけのプロセスをバックグラウンド実行させるという方法です。テスト全体の実行時間は time でわかるので、あとはテスト 1 件の開始と終了のタイミング(=カウント)がわかれば計算できます。テストの開始と終了の時点で、プロファイラプロセスに通知してその時のカウント値を記録させています。(最初はシグナルを用いて通知していましたが扱いが大変だったのでファイルを使用しています。), つまり CPU コア一つを全力でぶん回すというかなり強引な手段です。テストの実行時間に影響があるのでは?とか精度が悪いのでは?という懸念はありましたがそれなりに上手く言っているようです。(あまり意味がない機能だし・・・), バージョン 0.19.0でシングルスクリプトファイルのテストに対応しました。多くのシェルスクリプトは1ファイルの実行可能なスクリプトとして作成されていることが多いと思います。関数だけで定義されているライブラリファイルを読み込む場合は ShellSpec の Include で簡単に読み込んでテストできるのですが、実行可能なスクリプトファイルになっている場合テストが困難でした。, バージョン 0.19.0で対応した機能により、シェルスクリプトに Interception point を入れておくことでその場所で処理を割り込ませたり、特定の行でスクリプトを中断させる(Sourced Return)ことで、実行可能なスクリプトファイルのテストを可能にしました。, バージョン 0.20.0 でパラメータ化テストに対応しました。jUnit5 や rspec-parameterized にあるような機能でテストに対してパラメータを定義することで同様のテストをパラメータを変えて実行する機能です。, 実は当初の脳内設計には含まれていなくて、あとから追加したものなのですが、既存のテストのパラメータを定義するだけで簡単にパラメータ化テストに変更できるというシンプルな使い勝手を実現できたのでかなり気に入っています。(DSL はページの下の方を参照), github からコードを clone し、PATHが通った場所にシンボリックリンクを作成するだけです。, 通常はテスト対象のプロジェクトディレクトリがあると思うので、そのディレクトリ(なければ新規に作成してください)で shellspec --init を実行してください。スペックファイルを格納する spec ディレクトリと設定用のファイルが作成されます。, (とは言ってもこれらのファイルは必須ではなく、適当な場所にスペックファイルを作成して、shellspec に渡せば実行できるのですが・・・), あとは、spec ディレクトリの中にスペックファイルを作成していき、プロジェクトディレクトリ直下で shellspec を実行すれば、スペックファイルが実行されます。(スペックファイルの名前の末尾は _spec.sh である必要があります。), ShellSpec の DSL は大きく、Example group、Example、Evaluation、Expectation に分かれています。これらにあてはまらないものとして Helper と Directive があります。, BDD では一つのテストのことを実行可能な例(Example) と呼び、ShellSpec もそれに倣っています。, Example group は Example をグループ化するためのブロック構文です。ブロック構文とは特定のキーワードで始まり ザナルカンドにて 楽譜 原曲,
筋肉痛 治らない 老化,
Ff14 ウィンドウモード 枠,
ニテル サイト 安全,
賭ケグルイ 会長 夢子,
グランツーリスモ 4 フォーミュラ,
上田市 コロナ ツイッター,
サボン スクラブ 開け方 スプーン以外,
1 8いこうよ 見逃し,
24 シーズン4 テロリスト 女,
ヒゲダン 好きな人 特徴,
エニアグラム 相性 同性,
Premium Style ワイヤレスイヤホン ペアリング,
" />
と書くことで別のフィルタを間に入れるといったことも可能です。, これも ShellSpec 0.8.0 からの機能です。他の DSL とは少し違う扱いなので Directive という別の名前で呼んでいます。, これらを使用して先程の Before, After のサンプルコードを書き直すと以下のようになります。, % は %const の別名で定数を定義します。ただしこの定数の値はスペックファイルの変換時に決定されます。つまり ShellSpec の環境変数である $SHELLSPEC_TMPBASE などを参照することは出来ますが、スペックファイルの変数や関数は参照できません。, もともとこの機能は、条件付きスキップで必要になりました。通常は Example を実行の起点とし、Before 等で変数の初期化などを行うのですが、 Skip if は、Example 外で処理が実行されるため、まだ変数が初期化されていないわけです。そのため内部で作成する変数ではなく外部から与えられる定数という扱いで、Describe の外でファイルレベルの定数を定義できるようにしました。これは定数であるため名前は大文字限定としています。(これは ShellCheck の警告を抑制する効果もあります。)今はただの変数として実装されているためスペックファイル内で値の変更ができるのですが将来的には readonly にするかもしれません。, そしてもう一つの Directive が 埋め込みテキストである %text です。この Directive は他の DSL と違って関数の中で使用します。%text はシェルスクリプトのヒアドキュメントの代わりとなる機能を提供します。先程の setup 関数をヒアドキュメントを使用して書くと以下のようになります。, シェルスクリプトのヒアドキュメントはインデントできないため、インデントされたコードで非常に見づらいものとなってしまいます。(正確にはハードタブを使用すればインデントは可能なのですが、ヒアドキュメントの終わりはインデントできず、ぱっと見でタブとスペースの区別がつかないためいろいろと問題があります。), 先程の例では %text の内容をリダイレクトでファイルに出力しましたが、リダイレクトを書かなければ標準出力に出力されます。Data ブロックと同様、%text:expand で変数展開を行ったり別のフィルタと組み合わせることも出来ます。, Data ブロックの例を %text を使用すると以下のように書くことが出来ます。, テストではしばしばモックやスタブを使用する必要が出てきます。現在 ShellSpec にはそのための専用の機能はありませんが、関数を一時的に置き換えることで実現できます。, get_next_day 関数は内部で date コマンドを呼び出しています。しかし直接 date コマンドを呼び出してしまうと実行するたびに値が変わるためテストになりません。そのため date コマンドを date 関数で上書きしています。これはサブシェル内の一時的な上書きであることに注意してください。そのためサブシェルを抜けると元の date コマンドが呼び出されるようになります。, この例では実際には外部コマンドを関数で上書きしており、関数を関数で上書きしたわけではありませんが、例えば unixtime 関数をブロック内で再定義してもサブシェルを抜けると元に戻ります。, Parametersでパラメータを複数定義し、テストはパラメータの部分を $1, $2, ... で置き換えるだけで、簡単にパラメータ化テストに変更することが出来ます。, こうしてみると色々と機能があるのですが、スペックファイルの文法はシンプルで自然言語に近いため覚えやすく簡単に使えると思います。その他のサンプル もこちらに用意しています。shUnit2 や Bats は実際に使ってみるとわかると思いますが、テストコードを読むのにシェルスクリプトの知識が必要になります。ShellSpec ではそれが必要最小限に抑えられているためレビューも容易になると思います。, また殆どの処理をシェルの内部コマンドだけで行っており、外部コマンドの呼び出しをほとんど行わないため極めて軽快に動作します。(テスト内容によるためはっきりとは言えませんが、shUnit2 や Bats の2~5倍以上の速度で動作するようです。2019/04/09 追記 0.10.0でサポート予定の並列実行機能でさらに2倍程度高速化します。)高速にテストを実行できることもテストツールの重要な要件の一つです。, シェルスクリプトでテストコードが必要なほど大きなものは作らないという考え方もあるかと思いますがテストは重要です。もし必要になったときは思い出してぜひ使ってみてください。, おそらくウェブアプリエンジニア。フロントやったりサーバーやったりたまにインフラ。好きなもの:シンプルで無駄のないコード、リファクタリング。嫌いなもの:技術的負債、レガシーコード. script.sh # Unit tests shUnitでテストしたいシェルスクリプトがあります。 スクリプト(とすべての関数)は、インストールがはるかに簡単になるため、単一のファイルにあります。 ASSERT:【文字が引数】Nullでない, シェルスクリプトは性質上、上から下に処理が流れてしまうため、shUnit2を使用する場合は以下の構成にするのがよいと思います。, shUnit2はブラックボックステストになるので、ブラックボックスをなるべく小さくして、それぞれを検証していくスタイルのほうがテストの堅牢性を守りつつ、ミスの少なスクリプトが作れると思います。, BASH Debugger flk_test.func に定義されている関数は3つ。, ここでそれぞれの関数の依存関係を考慮します。依存性の小さい関数からテストをすることで、依存されている側(flt_testに於いては fla_test_main)の確認観点を絞ることができるためです。, 関数名を確認観点にすると、実際にテストを実行した時の見た目で何を確認したのが分かるようになります。, assertEquals と assertNull はshUnit2で用意されているアサーション関数です。こちらは他にもいろいろと関数があるのでマニュアルを参照してもらえればと思いますが、概ねJUnitと同じ考えです。, <<アサーション失敗時のメッセージ>>はアサーションが失敗した時に標準出力に出力されるので、被疑箇所の特定がしやすくなります。, ASSERT:【文字が引数】Nullでない シェルスクリプトのユニットテスト (5) . 例えば下記のようなコードのシェルを実行したときにrmコマンドを意図的に失敗させて「失敗」を表示させるにはどうすればよいでしょうか?rmコマンドは例えなので、rmコマンドに限らず他のコマンドでも共通で意図的に失敗させることができるような方法を教えてほしいです。 #!/bin/csh -frm /et 「ごりごりの複雑なロジックだから何かが起きそう」, そうすると機能テストをする流れになると思いますが、ざっと確認してみたところ、以下の方法が考えられそう。, 今回はタイトルの通り、shUnit2について取り上げたいと思います。Batsは気になるから別の機会に。, shUnit2は前項でも書いたとおり、シェルスクリプト向け自動テストフレームワークです。 shUnit2 2.1.x nドキュメント これらの組み合わせ 16 パターンについて、テストを実行するシェルスクリプトを作成します。 Param1 が D で、Param2 が c のときの処理にバグがあって、テストが失敗するものとします。 単純に for を2重にループさせ、テストプログラムを実行することにします。 Endで終わる構文です。これは ShellSpec DSL の拡張構文で純粋なシェルスクリプトの構文ではありません。(シェルスクリプトとしてみると開始と終わりはそれぞれただの 1 命令として扱われます。), Example group のDSLには Describe と Context があり、機能的にはどちらも同じですが、テスト対象についての説明を行う時に Describe を使用し、テストの状況を説明するときには Context を使用します。これらはネスト可能で、テスト対象や状況に応じて構造化してグループ化することが出来ます。, 実行可能な例 (Example) です。このブロックの中に、何をして (Evaluation) どうあるべきか?(Expectation) を記述します。, Example group の DSL には ブロック構文の Example、Specify、It、ワンライナー構文の Todo があります。It を使って書くと説明をより自然な文章にすることが出来ますが、しっくりこない場合には、同等の機能である Example や Specify を使用することができます。Todo は実装予定の例を一行で記述するための DSLで、内容がない Example と同等の意味になります。, Example の中の When で始まる行が Evaluation です。Evaluation でテスト対象のシェル関数や外部コマンドを呼び出します。Evaluation は一つの Example の中に一つしか書けません。(省略することは出来ます。), When の次の call はシェル関数もしくは外部コマンドを呼び出すことを意味し、他に外部コマンドのみを呼び出す run やサブシェル内で呼び出す invoke (特殊な用途用なのであまり使いません)があります。一般的には call を使用すれば十分でしょう。, Example の中の The で始まる行が Expectation です。Evaluation の実行結果を検証します。Expectation は複数書くことが出来ます。複数書いた場合でも途中の失敗で停止することなくすべての Expectation が実行されます。(RSpec の aggregate_failures 相当の動作です。), should の代わりに should not を使用することで、否定の意味にすることも出来ます。, 標準出力の2行目を検証したい等といった場合に使用するのが modifier です。 modifier は subject の内容を加工し新たな subject とします。, modifier はつなげて書くことも出来ます。また数値は序数で書くことも出来ます。, subject、modifier、matcher には、標準エラー出力や終了ステータスや検証するための subject やファイルの状態や文字列が特定のパターンにマッチしているかを検証する matcher など、他にも色々あります。詳細はプロジェクトサイトを参照してください。, 以下は、Include、Before、After を使用したサンプルです。 #!/bin/sh echo $1 シェルスクリプトでも単体テストを書いてCIした方が精神衛生上好ましいなと思い、調べてみるとshUnit2という単体テストフレームワークがあったので、これを使って単体テストとCIを実施し … kcov スクリプトの作成し終えたら、単体テストをおこないます。 想定した前提条件で スクリプトを実行して、想定した結果となることを確認する「正常系」のテストと ifやwhileなどで分岐やループをしているすべての箇所が、想定した動作になっていることを確認する「異常系」のテスト … ShellSpec - シェルスクリプト用のフル機能のBDDユニットテストフレームワーク, ShellSpec は BDD スタイルのテスティングフレームワークです。他の BDD スタイルのテスティングフレームワークと同様に、自然言語に近い DSL で構造化されたテスト(実行可能な例)を記述することができます。, POSIX 互換のシェルスクリプトで実装されており、依存している外部コマンドも POSIX 準拠の基本的なものだけなので bash だけでなく dash や zsh や ksh などより多くのシェルで動作します。OS も様々なものに対応しており、Debian, macOS, Soralis, Windows(WSL)や Docker 上の Alpine Linux や組み込みで使われることが多い BusyBox でもそのまま動きます。, RSpec に大きく影響を受けており、似ている機能や設計でありながら、シェルスクリプトの用途に適した DSL となっています。, 「123 は 123である」というあまり意味がない例ですが、シェルスクリプトを知らなくとも自然言語(英語ですが・・・)として読むことが出来ます。, 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 形式に対応しています。, テスト実行の際に、行番号、ID、フォーカス、テスト名、タグ、といった様々な方法で実行するテストを指定することが出来ます。一番使用頻度が多いと思われる行番号は、(RSpecのように)以下の方法で指定することが出来ます。, またテストの、Descript や It をそれぞれ fDescribe、fIt と書き換えることでフォーカスを指定し、フォーカスされたテストだけを実行することが出来ます。これも RSpec を参考に実装したのですが、RSpecとは違い実行するときに --focus オプションが必要です。(指定しない場合はすべて実行されます), これは ShellSpec の設計上 1 パス、つまりスペックファイルを変換しながらそのまま実行しているため、最初にスペックファイルにフォーカスされた行があるか知ることが不可能だからです。--focus オプションが必要なのは制限とも思えるのですが実際のテスト修正では、エラーとなった行を修正 → フォーカスしたものだけを実行 → 全体を実行して確認 → エラーが修正されていなければその箇所のみ修正 → 繰り返す と フォーカスしたものと全体の実行を交互に行うと思うので、これはこれで良かったかなぁと思っています。, バージョン 0.10.0 で並列実行に対応しました。並列実行は Bats-core でも対応予定(おそらく1.2.0から。master には含まれています)ですが、 Bats-core では GNU parallel が必要なのに対して ShellSpec はシェルスクリプトのバックグラウンドプロセスを利用しシェルスクリプトのみで実装しています。そのためすべてのシェルで利用することが出来ます。, バージョン 0.14.0 でスペックファイルのランダム実行に対応しました。これもシェルスクリプト(とsortとodコマンド)のみで実行しています。実行順のランダム化には fnv1a で(od コマンドで 8 進数に変換した)ファイル名をハッシュ化し xorshift32 でハッシュ値からランダム値を求めてsortしています。, ハッシュを求めるコマンドは cksum (や sha1sum、md5sum等)を使用することも出来たのですが、これだとファイルの数だけコマンド呼び出しが発生するので遅くなります。また $RANDOM や /dev/urandom でランダム値を取得することも可能ですが、シェルや OS に依存します。そのためハッシュ化とランダム値の計算をシェルスクリプトで実装しました。どちらも軽量なアルゴリズムのためシェルスクリプトで実装しても特に遅さは感じません。, バージョン 0.16.0 でkcov を統合させることにより、カバレッジ機能に対応させました。もちろんカバレッジレポートも出力できるのでテストを行っていない箇所を可視化することが出来ます。, どのテスティングフレームワークにも言えることですがカバレッジ機能がフレームワークに統合されていない場合、それを自分で統合させるのはけっこう大変な作業です。ShellSpec では設計上「Kcov 上で ShellSpecを実行」するだけではカバレッジを取ることが出来ないため、ShellSpec に統合させました。, なおカバレッジを取ることが出来るシェルは bash のみです。(Kcov は bash のみに対応しており、Kcov は bash のデバッグ機能に依存しています。)これが今の所唯一の特定のシェルでしか使えない機能となります。 bash, zsh, ksh です。Kcov は bash しか対応していませんが、bash と同様の出力を行うことで、zsh と ksh に対応させました。(2020/05/11), バージョン 0.18.0 でプロファイラに対応しています。時間がかかっているテストをリストアップすることができます。が、はっきり言ってこれは一番意味がない機能だと思っています(笑), アプリケーションのプロファイラならともかく、テストのプロファイラにどういう意味があるのでしょうか?時間がかかっているテストだから短くしようという意味がなくはないですが、テストで時間がかかっているからと言って、実際のアプリケーションではボトルネックになっていなかったり、逆にテストでは短い時間でも実際のアプリケーションでは何度も呼び出すためボトルネックになるということがあるのでテストでプロファイラを使用してもあまり意味がないと思っています。, ではなぜ実装したのか?ですが、jUnit XML で(必須ではないですが)テスト実行時間の項目があるからです。というのは建前で、面白い実装方法を思いつたからです。, 多くの環境をサポートしようとするとシェルスクリプトでミリ秒の取得ができないという制限にぶち当たります。date コマンドは POSIX の範囲ではミリ秒の取得ができません。POSIX の範囲に限定するとシェルスクリプトでミリ秒を取得できるコマンドは time と times のみです。このうち times は user 時間と sys 時間は取得できますが肝心の real 時間が取得できません。そのため使えるコマンドは time のみです。しかし time は呼び出したコマンドの実行時間を調べるものなので、テスト一つごとに外部プロセスとして呼び出さなければいけません。テストでシェル関数呼び出しなどを行うのでそれは不可能です。, そこでとった手段が、プロファイリングのために数値をカウントするだけのプロセスをバックグラウンド実行させるという方法です。テスト全体の実行時間は time でわかるので、あとはテスト 1 件の開始と終了のタイミング(=カウント)がわかれば計算できます。テストの開始と終了の時点で、プロファイラプロセスに通知してその時のカウント値を記録させています。(最初はシグナルを用いて通知していましたが扱いが大変だったのでファイルを使用しています。), つまり CPU コア一つを全力でぶん回すというかなり強引な手段です。テストの実行時間に影響があるのでは?とか精度が悪いのでは?という懸念はありましたがそれなりに上手く言っているようです。(あまり意味がない機能だし・・・), バージョン 0.19.0でシングルスクリプトファイルのテストに対応しました。多くのシェルスクリプトは1ファイルの実行可能なスクリプトとして作成されていることが多いと思います。関数だけで定義されているライブラリファイルを読み込む場合は ShellSpec の Include で簡単に読み込んでテストできるのですが、実行可能なスクリプトファイルになっている場合テストが困難でした。, バージョン 0.19.0で対応した機能により、シェルスクリプトに Interception point を入れておくことでその場所で処理を割り込ませたり、特定の行でスクリプトを中断させる(Sourced Return)ことで、実行可能なスクリプトファイルのテストを可能にしました。, バージョン 0.20.0 でパラメータ化テストに対応しました。jUnit5 や rspec-parameterized にあるような機能でテストに対してパラメータを定義することで同様のテストをパラメータを変えて実行する機能です。, 実は当初の脳内設計には含まれていなくて、あとから追加したものなのですが、既存のテストのパラメータを定義するだけで簡単にパラメータ化テストに変更できるというシンプルな使い勝手を実現できたのでかなり気に入っています。(DSL はページの下の方を参照), github からコードを clone し、PATHが通った場所にシンボリックリンクを作成するだけです。, 通常はテスト対象のプロジェクトディレクトリがあると思うので、そのディレクトリ(なければ新規に作成してください)で shellspec --init を実行してください。スペックファイルを格納する spec ディレクトリと設定用のファイルが作成されます。, (とは言ってもこれらのファイルは必須ではなく、適当な場所にスペックファイルを作成して、shellspec に渡せば実行できるのですが・・・), あとは、spec ディレクトリの中にスペックファイルを作成していき、プロジェクトディレクトリ直下で shellspec を実行すれば、スペックファイルが実行されます。(スペックファイルの名前の末尾は _spec.sh である必要があります。), ShellSpec の DSL は大きく、Example group、Example、Evaluation、Expectation に分かれています。これらにあてはまらないものとして Helper と Directive があります。, BDD では一つのテストのことを実行可能な例(Example) と呼び、ShellSpec もそれに倣っています。, Example group は Example をグループ化するためのブロック構文です。ブロック構文とは特定のキーワードで始まり ザナルカンドにて 楽譜 原曲,
筋肉痛 治らない 老化,
Ff14 ウィンドウモード 枠,
ニテル サイト 安全,
賭ケグルイ 会長 夢子,
グランツーリスモ 4 フォーミュラ,
上田市 コロナ ツイッター,
サボン スクラブ 開け方 スプーン以外,
1 8いこうよ 見逃し,
24 シーズン4 テロリスト 女,
ヒゲダン 好きな人 特徴,
エニアグラム 相性 同性,
Premium Style ワイヤレスイヤホン ペアリング,
" />
と書くことで別のフィルタを間に入れるといったことも可能です。, これも ShellSpec 0.8.0 からの機能です。他の DSL とは少し違う扱いなので Directive という別の名前で呼んでいます。, これらを使用して先程の Before, After のサンプルコードを書き直すと以下のようになります。, % は %const の別名で定数を定義します。ただしこの定数の値はスペックファイルの変換時に決定されます。つまり ShellSpec の環境変数である $SHELLSPEC_TMPBASE などを参照することは出来ますが、スペックファイルの変数や関数は参照できません。, もともとこの機能は、条件付きスキップで必要になりました。通常は Example を実行の起点とし、Before 等で変数の初期化などを行うのですが、 Skip if は、Example 外で処理が実行されるため、まだ変数が初期化されていないわけです。そのため内部で作成する変数ではなく外部から与えられる定数という扱いで、Describe の外でファイルレベルの定数を定義できるようにしました。これは定数であるため名前は大文字限定としています。(これは ShellCheck の警告を抑制する効果もあります。)今はただの変数として実装されているためスペックファイル内で値の変更ができるのですが将来的には readonly にするかもしれません。, そしてもう一つの Directive が 埋め込みテキストである %text です。この Directive は他の DSL と違って関数の中で使用します。%text はシェルスクリプトのヒアドキュメントの代わりとなる機能を提供します。先程の setup 関数をヒアドキュメントを使用して書くと以下のようになります。, シェルスクリプトのヒアドキュメントはインデントできないため、インデントされたコードで非常に見づらいものとなってしまいます。(正確にはハードタブを使用すればインデントは可能なのですが、ヒアドキュメントの終わりはインデントできず、ぱっと見でタブとスペースの区別がつかないためいろいろと問題があります。), 先程の例では %text の内容をリダイレクトでファイルに出力しましたが、リダイレクトを書かなければ標準出力に出力されます。Data ブロックと同様、%text:expand で変数展開を行ったり別のフィルタと組み合わせることも出来ます。, Data ブロックの例を %text を使用すると以下のように書くことが出来ます。, テストではしばしばモックやスタブを使用する必要が出てきます。現在 ShellSpec にはそのための専用の機能はありませんが、関数を一時的に置き換えることで実現できます。, get_next_day 関数は内部で date コマンドを呼び出しています。しかし直接 date コマンドを呼び出してしまうと実行するたびに値が変わるためテストになりません。そのため date コマンドを date 関数で上書きしています。これはサブシェル内の一時的な上書きであることに注意してください。そのためサブシェルを抜けると元の date コマンドが呼び出されるようになります。, この例では実際には外部コマンドを関数で上書きしており、関数を関数で上書きしたわけではありませんが、例えば unixtime 関数をブロック内で再定義してもサブシェルを抜けると元に戻ります。, Parametersでパラメータを複数定義し、テストはパラメータの部分を $1, $2, ... で置き換えるだけで、簡単にパラメータ化テストに変更することが出来ます。, こうしてみると色々と機能があるのですが、スペックファイルの文法はシンプルで自然言語に近いため覚えやすく簡単に使えると思います。その他のサンプル もこちらに用意しています。shUnit2 や Bats は実際に使ってみるとわかると思いますが、テストコードを読むのにシェルスクリプトの知識が必要になります。ShellSpec ではそれが必要最小限に抑えられているためレビューも容易になると思います。, また殆どの処理をシェルの内部コマンドだけで行っており、外部コマンドの呼び出しをほとんど行わないため極めて軽快に動作します。(テスト内容によるためはっきりとは言えませんが、shUnit2 や Bats の2~5倍以上の速度で動作するようです。2019/04/09 追記 0.10.0でサポート予定の並列実行機能でさらに2倍程度高速化します。)高速にテストを実行できることもテストツールの重要な要件の一つです。, シェルスクリプトでテストコードが必要なほど大きなものは作らないという考え方もあるかと思いますがテストは重要です。もし必要になったときは思い出してぜひ使ってみてください。, おそらくウェブアプリエンジニア。フロントやったりサーバーやったりたまにインフラ。好きなもの:シンプルで無駄のないコード、リファクタリング。嫌いなもの:技術的負債、レガシーコード. script.sh # Unit tests shUnitでテストしたいシェルスクリプトがあります。 スクリプト(とすべての関数)は、インストールがはるかに簡単になるため、単一のファイルにあります。 ASSERT:【文字が引数】Nullでない, シェルスクリプトは性質上、上から下に処理が流れてしまうため、shUnit2を使用する場合は以下の構成にするのがよいと思います。, shUnit2はブラックボックステストになるので、ブラックボックスをなるべく小さくして、それぞれを検証していくスタイルのほうがテストの堅牢性を守りつつ、ミスの少なスクリプトが作れると思います。, BASH Debugger flk_test.func に定義されている関数は3つ。, ここでそれぞれの関数の依存関係を考慮します。依存性の小さい関数からテストをすることで、依存されている側(flt_testに於いては fla_test_main)の確認観点を絞ることができるためです。, 関数名を確認観点にすると、実際にテストを実行した時の見た目で何を確認したのが分かるようになります。, assertEquals と assertNull はshUnit2で用意されているアサーション関数です。こちらは他にもいろいろと関数があるのでマニュアルを参照してもらえればと思いますが、概ねJUnitと同じ考えです。, <<アサーション失敗時のメッセージ>>はアサーションが失敗した時に標準出力に出力されるので、被疑箇所の特定がしやすくなります。, ASSERT:【文字が引数】Nullでない シェルスクリプトのユニットテスト (5) . 例えば下記のようなコードのシェルを実行したときにrmコマンドを意図的に失敗させて「失敗」を表示させるにはどうすればよいでしょうか?rmコマンドは例えなので、rmコマンドに限らず他のコマンドでも共通で意図的に失敗させることができるような方法を教えてほしいです。 #!/bin/csh -frm /et 「ごりごりの複雑なロジックだから何かが起きそう」, そうすると機能テストをする流れになると思いますが、ざっと確認してみたところ、以下の方法が考えられそう。, 今回はタイトルの通り、shUnit2について取り上げたいと思います。Batsは気になるから別の機会に。, shUnit2は前項でも書いたとおり、シェルスクリプト向け自動テストフレームワークです。 shUnit2 2.1.x nドキュメント これらの組み合わせ 16 パターンについて、テストを実行するシェルスクリプトを作成します。 Param1 が D で、Param2 が c のときの処理にバグがあって、テストが失敗するものとします。 単純に for を2重にループさせ、テストプログラムを実行することにします。 Endで終わる構文です。これは ShellSpec DSL の拡張構文で純粋なシェルスクリプトの構文ではありません。(シェルスクリプトとしてみると開始と終わりはそれぞれただの 1 命令として扱われます。), Example group のDSLには Describe と Context があり、機能的にはどちらも同じですが、テスト対象についての説明を行う時に Describe を使用し、テストの状況を説明するときには Context を使用します。これらはネスト可能で、テスト対象や状況に応じて構造化してグループ化することが出来ます。, 実行可能な例 (Example) です。このブロックの中に、何をして (Evaluation) どうあるべきか?(Expectation) を記述します。, Example group の DSL には ブロック構文の Example、Specify、It、ワンライナー構文の Todo があります。It を使って書くと説明をより自然な文章にすることが出来ますが、しっくりこない場合には、同等の機能である Example や Specify を使用することができます。Todo は実装予定の例を一行で記述するための DSLで、内容がない Example と同等の意味になります。, Example の中の When で始まる行が Evaluation です。Evaluation でテスト対象のシェル関数や外部コマンドを呼び出します。Evaluation は一つの Example の中に一つしか書けません。(省略することは出来ます。), When の次の call はシェル関数もしくは外部コマンドを呼び出すことを意味し、他に外部コマンドのみを呼び出す run やサブシェル内で呼び出す invoke (特殊な用途用なのであまり使いません)があります。一般的には call を使用すれば十分でしょう。, Example の中の The で始まる行が Expectation です。Evaluation の実行結果を検証します。Expectation は複数書くことが出来ます。複数書いた場合でも途中の失敗で停止することなくすべての Expectation が実行されます。(RSpec の aggregate_failures 相当の動作です。), should の代わりに should not を使用することで、否定の意味にすることも出来ます。, 標準出力の2行目を検証したい等といった場合に使用するのが modifier です。 modifier は subject の内容を加工し新たな subject とします。, modifier はつなげて書くことも出来ます。また数値は序数で書くことも出来ます。, subject、modifier、matcher には、標準エラー出力や終了ステータスや検証するための subject やファイルの状態や文字列が特定のパターンにマッチしているかを検証する matcher など、他にも色々あります。詳細はプロジェクトサイトを参照してください。, 以下は、Include、Before、After を使用したサンプルです。 #!/bin/sh echo $1 シェルスクリプトでも単体テストを書いてCIした方が精神衛生上好ましいなと思い、調べてみるとshUnit2という単体テストフレームワークがあったので、これを使って単体テストとCIを実施し … kcov スクリプトの作成し終えたら、単体テストをおこないます。 想定した前提条件で スクリプトを実行して、想定した結果となることを確認する「正常系」のテストと ifやwhileなどで分岐やループをしているすべての箇所が、想定した動作になっていることを確認する「異常系」のテスト … ShellSpec - シェルスクリプト用のフル機能のBDDユニットテストフレームワーク, ShellSpec は BDD スタイルのテスティングフレームワークです。他の BDD スタイルのテスティングフレームワークと同様に、自然言語に近い DSL で構造化されたテスト(実行可能な例)を記述することができます。, POSIX 互換のシェルスクリプトで実装されており、依存している外部コマンドも POSIX 準拠の基本的なものだけなので bash だけでなく dash や zsh や ksh などより多くのシェルで動作します。OS も様々なものに対応しており、Debian, macOS, Soralis, Windows(WSL)や Docker 上の Alpine Linux や組み込みで使われることが多い BusyBox でもそのまま動きます。, RSpec に大きく影響を受けており、似ている機能や設計でありながら、シェルスクリプトの用途に適した DSL となっています。, 「123 は 123である」というあまり意味がない例ですが、シェルスクリプトを知らなくとも自然言語(英語ですが・・・)として読むことが出来ます。, 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 形式に対応しています。, テスト実行の際に、行番号、ID、フォーカス、テスト名、タグ、といった様々な方法で実行するテストを指定することが出来ます。一番使用頻度が多いと思われる行番号は、(RSpecのように)以下の方法で指定することが出来ます。, またテストの、Descript や It をそれぞれ fDescribe、fIt と書き換えることでフォーカスを指定し、フォーカスされたテストだけを実行することが出来ます。これも RSpec を参考に実装したのですが、RSpecとは違い実行するときに --focus オプションが必要です。(指定しない場合はすべて実行されます), これは ShellSpec の設計上 1 パス、つまりスペックファイルを変換しながらそのまま実行しているため、最初にスペックファイルにフォーカスされた行があるか知ることが不可能だからです。--focus オプションが必要なのは制限とも思えるのですが実際のテスト修正では、エラーとなった行を修正 → フォーカスしたものだけを実行 → 全体を実行して確認 → エラーが修正されていなければその箇所のみ修正 → 繰り返す と フォーカスしたものと全体の実行を交互に行うと思うので、これはこれで良かったかなぁと思っています。, バージョン 0.10.0 で並列実行に対応しました。並列実行は Bats-core でも対応予定(おそらく1.2.0から。master には含まれています)ですが、 Bats-core では GNU parallel が必要なのに対して ShellSpec はシェルスクリプトのバックグラウンドプロセスを利用しシェルスクリプトのみで実装しています。そのためすべてのシェルで利用することが出来ます。, バージョン 0.14.0 でスペックファイルのランダム実行に対応しました。これもシェルスクリプト(とsortとodコマンド)のみで実行しています。実行順のランダム化には fnv1a で(od コマンドで 8 進数に変換した)ファイル名をハッシュ化し xorshift32 でハッシュ値からランダム値を求めてsortしています。, ハッシュを求めるコマンドは cksum (や sha1sum、md5sum等)を使用することも出来たのですが、これだとファイルの数だけコマンド呼び出しが発生するので遅くなります。また $RANDOM や /dev/urandom でランダム値を取得することも可能ですが、シェルや OS に依存します。そのためハッシュ化とランダム値の計算をシェルスクリプトで実装しました。どちらも軽量なアルゴリズムのためシェルスクリプトで実装しても特に遅さは感じません。, バージョン 0.16.0 でkcov を統合させることにより、カバレッジ機能に対応させました。もちろんカバレッジレポートも出力できるのでテストを行っていない箇所を可視化することが出来ます。, どのテスティングフレームワークにも言えることですがカバレッジ機能がフレームワークに統合されていない場合、それを自分で統合させるのはけっこう大変な作業です。ShellSpec では設計上「Kcov 上で ShellSpecを実行」するだけではカバレッジを取ることが出来ないため、ShellSpec に統合させました。, なおカバレッジを取ることが出来るシェルは bash のみです。(Kcov は bash のみに対応しており、Kcov は bash のデバッグ機能に依存しています。)これが今の所唯一の特定のシェルでしか使えない機能となります。 bash, zsh, ksh です。Kcov は bash しか対応していませんが、bash と同様の出力を行うことで、zsh と ksh に対応させました。(2020/05/11), バージョン 0.18.0 でプロファイラに対応しています。時間がかかっているテストをリストアップすることができます。が、はっきり言ってこれは一番意味がない機能だと思っています(笑), アプリケーションのプロファイラならともかく、テストのプロファイラにどういう意味があるのでしょうか?時間がかかっているテストだから短くしようという意味がなくはないですが、テストで時間がかかっているからと言って、実際のアプリケーションではボトルネックになっていなかったり、逆にテストでは短い時間でも実際のアプリケーションでは何度も呼び出すためボトルネックになるということがあるのでテストでプロファイラを使用してもあまり意味がないと思っています。, ではなぜ実装したのか?ですが、jUnit XML で(必須ではないですが)テスト実行時間の項目があるからです。というのは建前で、面白い実装方法を思いつたからです。, 多くの環境をサポートしようとするとシェルスクリプトでミリ秒の取得ができないという制限にぶち当たります。date コマンドは POSIX の範囲ではミリ秒の取得ができません。POSIX の範囲に限定するとシェルスクリプトでミリ秒を取得できるコマンドは time と times のみです。このうち times は user 時間と sys 時間は取得できますが肝心の real 時間が取得できません。そのため使えるコマンドは time のみです。しかし time は呼び出したコマンドの実行時間を調べるものなので、テスト一つごとに外部プロセスとして呼び出さなければいけません。テストでシェル関数呼び出しなどを行うのでそれは不可能です。, そこでとった手段が、プロファイリングのために数値をカウントするだけのプロセスをバックグラウンド実行させるという方法です。テスト全体の実行時間は time でわかるので、あとはテスト 1 件の開始と終了のタイミング(=カウント)がわかれば計算できます。テストの開始と終了の時点で、プロファイラプロセスに通知してその時のカウント値を記録させています。(最初はシグナルを用いて通知していましたが扱いが大変だったのでファイルを使用しています。), つまり CPU コア一つを全力でぶん回すというかなり強引な手段です。テストの実行時間に影響があるのでは?とか精度が悪いのでは?という懸念はありましたがそれなりに上手く言っているようです。(あまり意味がない機能だし・・・), バージョン 0.19.0でシングルスクリプトファイルのテストに対応しました。多くのシェルスクリプトは1ファイルの実行可能なスクリプトとして作成されていることが多いと思います。関数だけで定義されているライブラリファイルを読み込む場合は ShellSpec の Include で簡単に読み込んでテストできるのですが、実行可能なスクリプトファイルになっている場合テストが困難でした。, バージョン 0.19.0で対応した機能により、シェルスクリプトに Interception point を入れておくことでその場所で処理を割り込ませたり、特定の行でスクリプトを中断させる(Sourced Return)ことで、実行可能なスクリプトファイルのテストを可能にしました。, バージョン 0.20.0 でパラメータ化テストに対応しました。jUnit5 や rspec-parameterized にあるような機能でテストに対してパラメータを定義することで同様のテストをパラメータを変えて実行する機能です。, 実は当初の脳内設計には含まれていなくて、あとから追加したものなのですが、既存のテストのパラメータを定義するだけで簡単にパラメータ化テストに変更できるというシンプルな使い勝手を実現できたのでかなり気に入っています。(DSL はページの下の方を参照), github からコードを clone し、PATHが通った場所にシンボリックリンクを作成するだけです。, 通常はテスト対象のプロジェクトディレクトリがあると思うので、そのディレクトリ(なければ新規に作成してください)で shellspec --init を実行してください。スペックファイルを格納する spec ディレクトリと設定用のファイルが作成されます。, (とは言ってもこれらのファイルは必須ではなく、適当な場所にスペックファイルを作成して、shellspec に渡せば実行できるのですが・・・), あとは、spec ディレクトリの中にスペックファイルを作成していき、プロジェクトディレクトリ直下で shellspec を実行すれば、スペックファイルが実行されます。(スペックファイルの名前の末尾は _spec.sh である必要があります。), ShellSpec の DSL は大きく、Example group、Example、Evaluation、Expectation に分かれています。これらにあてはまらないものとして Helper と Directive があります。, BDD では一つのテストのことを実行可能な例(Example) と呼び、ShellSpec もそれに倣っています。, Example group は Example をグループ化するためのブロック構文です。ブロック構文とは特定のキーワードで始まり ザナルカンドにて 楽譜 原曲,
筋肉痛 治らない 老化,
Ff14 ウィンドウモード 枠,
ニテル サイト 安全,
賭ケグルイ 会長 夢子,
グランツーリスモ 4 フォーミュラ,
上田市 コロナ ツイッター,
サボン スクラブ 開け方 スプーン以外,
1 8いこうよ 見逃し,
24 シーズン4 テロリスト 女,
ヒゲダン 好きな人 特徴,
エニアグラム 相性 同性,
Premium Style ワイヤレスイヤホン ペアリング,
" />
シェル スクリプト unit テスト
/EXECUTABLE/PATH/ = /usr/local/bin/, $HOME/bin/), # date is not redefined because this is another subshell, # block style (default: same as Parameters), ShellSpec - シェルスクリプト用のフル機能のBDDユニットテストフレームワーク, you can read useful information later efficiently. 実はシェルスクリプト以外にもユーザーデータを使用しての自動化は行えるが、今回は一番ポピュラーなシェルスクリプトを使用させていただいた。 上記でも書いたがこういうことを構築時に自動化したい等あれば是非コメント等いただきたい。 シェルスクリプトでもtddするためにテストコードを書いてカバレッジ測定をする。 テストにはshunit2を使用し、カバレッジ測定にはshcovを利用する。 shunit2 shcov は利用が簡単のためおすすめだが、メンテナンスが十分されるかどうかが心配。 shcov unixのコマンドをファイルに書いておき、バッチとして実行することが出来る。(ms-dosのバッチファイルに相当) ただ単にコマンドを羅列するだけでなく、スクリプト言語を使ってプログラミングできる。 (2020/09/17追記) この記事を補完・更新する記事として以下の記事を書きました 。 私が長年にわたって取り組んできた製品のほとんどは、あるレベルのシェルスクリプト(またはバッチファイル、Windows上のPowerShellなど)を含ん … flk_testという機能をテストしていきます。, src配下にあるのがテスト対象の実装、 test_flk_test.shunit2 は今回実装するテストコードです。, flk_test.sh はブートローダ的な役割のコードです。flk_test.funcをロードして、flk_test_main関数を呼び出すだけです。, flk_test.func は flk_test 機能の核となるファイルです。main関数である flk_test_main 関数、引き算をする flk_test_minus 、割り算をする flk_test_divide が実装されています。, では flk_test 機能に対してテストコードを書いていきますが、まずは固有のテストコードを含まないソースを見て、shUnit2が提供する関数を確認しましょう。, oneTimeSetUp 関数はテストコード実行時に最初の一回呼び出されるものです。テスト前の環境整備や、テスト出力のヘッダを表示させるのに使用できます。 シェルスクリプトからの関数のインポート (2) ... #!/bin/bash . shunit2 でshUnit2をロードし、テストを開始します。, 今回は、oneTimeSetUpとoneTimeTearDownでヘッダ・フッタを出力し、oneTimeSetUpでは flk_test.funcを読み込ませます。, テストコードの下準備が済んだので、テスト関数を実装していきます。 What is going on with this article? シェルスクリプト記述で、まず初めに覚えなければならないのは次の記述 #!/bin/sh # はhash、! Help us understand the problem. Why not register and get more from Qiita? スクリプトのutをする際に、スクリプト内の一部のコマンドを実行させずに正常終了だけ返すようにスタブ化したい時がよくある。そんな時のためにコマンドのスタブ化の手順について説明する。 【コマンド実行の仕組み】 そもそもコマンド実行の仕組みを テストは、対象のプログラムが仕様通りに正しく動いていることの確認や、バグを発見するために必要な工程です。適切なテストをすることにより、対象物(システム)の品質を担保できます。では、Ansibleのような構成管理ツールではどうでしょうか。従来の構成管理の手法としてはシェルスクリプトなどを使い、ミドルウェアのインストールおよびサービスの起動を行っていました。シェルスクリプトはプログラムなので、仕様通りに構成管理が行われていることの確認 … まだ shUnit2 や Bats (or Bats-core) で消耗してるの?なんてタイトルで釣ろうかと思いましたが、おとなしめのタイトルにしました。(笑), 前回は ShellSpec の使い方は README.md にまかせて開発したときの思いの丈(?)を書いたのですが、先日 ShellSpec をアップデートし大きく機能強化したのを機に、今回は使い方と新機能を紹介したいと思います。, (2019/08/23追記) 0.20.0 までの機能を追記しました。 というところから、何を指標にどのようにテストするのか体系的に解説いただき、最終的にはテストケースを考え、単体テストを … シェルスクリプトはその手軽さからちょちょっと書いてちょちょっと実行、なんてのが多いと思います。 でもそんなシェルスクリプトであっても、 自動化 単体テスト テスト実行 テストフレームワーク テストツール テスト シェルスクリプト カバレッジ unit test bats bash testing tdd automated-tests extreme-programming とりあえず実行してみる。 Pester.psm1の読み込みは先ほどやったので省略。 Invoke-Pesterでテストファイルを読み込ませる。 [+]緑なら期待通りの結果、[-]赤なら期待とは違うという意味。 赤いのでテスト失敗。 Test1.Test.ps1を修正する … 実行 ひな形のテストコード実行. [シェルスクリプト]ランダムな数値 では乱数の生成をしたけど、改めてシェルを使ってのテストデータ生成を考えてみよう。 できるだけ、デフォルトでありそうなコマンドかつ移植性のありそうな方法で。 同じ文字列の繰り返し 同じデータを繰り返し生成する場合はyesコマンドに文字列 … 以前、bashスクリプトをテストする仕事に取り組んだことがあります。最初、Pythonユニットテストを使うことにしましたが、プロジェクトに外部技術を持ち込むのは気が進みませんでした。そこで、仕方なく、悪名高いbashで書かれたテスト用フレームワークを使いま … unixシェルスクリプト. このスクリプトを整理すると、主要部分は4行程度にまとめられます。 #!/bin/sh # テキストファイルを端末に表示して、 # スクロールっぽいことをするテスト # # (3) 行単位で書き換える # スクリプトを整理する。 By following users and tags, you can catch up information on technical fields that you are interested in as a whole, By "stocking" the articles you like, you can search right away. 以前のエントリーに引き続き、シェルスクリプト ... Unitテストに関してはxUnit一択だと思いますが、UI系のテストツールについて。 IDE(コードを書かずにすむマクロ系)に関して全てChromeで動くことを確認しています。 Contents1 ツ … 昨日fbに探索的テストの話を書いたら、コメントくれた人が色々な使い方を言ってくれたのでちょっとまとめてみました。 イメージで語らずに具体化すると違いが出て面白ですね。 『探索的テストはスクリプトテストと併用できる』って言ったら、テストクラスタの人は『それは同意! ちなみにgitクローンを利用すると、執筆当時はv2.1.7preを導入できます。, 要は展開して終わりです。必要に応じてPATHを通したり、shunit2のファイルを /usr/local/bin に複製するなりしてください。, サンプルとして取り扱うディレクトリ構成、シェルスクリプト、テストコードの構成を示します。 「シェル」や「シェルスクリプト」という用語を聞いたことはありますか? 「シェル」とは、cli上で動作してosを操作できるソフトウェアのこと。そのシェルを組み合わせて記述する、簡単なプログラムを「シェルスクリプト」と言いま […] Shell Script Advent Calendar 2017 16日目です。 シェルのユニットテストを通して、自動 rm -rf / を防ぎましょう。, シェルスクリプトはその手軽さからちょちょっと書いてちょちょっと実行、なんてのが多いと思います。 By following users and tags, you can catch up information on technical fields that you are interested in as a whole, By "stocking" the articles you like, you can search right away. (mylib.sh の中にはデータファイルの件数を数える count 関数と合計を求める sum 関数があると考えてください), Before、After はそれぞれ、各 Example 実行の前後に呼び出されるフック関数(またはコード)を指定できます。初期化処理や終了処理を書く時に使用します。, Before、After で指定した関数が実行されるのは Example 毎であることに注意してください。つまりこの例の場合、setup 関数と cleanup 関数はそれぞれ2回ずつ呼び出されます。, Include も内部的にBefore 相当のコードになるため、Example 毎に読み込まれます。(2019/03/28 追記 関数の再定義を直感的に記述できないため 0.9.0 でその場で読み込むように仕様変更します、), スペック全体や Describe 単位で呼び出されるいわゆる BeforeAll や BeforeContext に相当する機能は意図的に実装していません。テストの独立性を壊す可能性があるためです。(とはいえ、スペックファイルはシェルスクリプトとして実行されるので実行したい箇所にコードを書けば普通に動くわけですが・・・。推奨はしません。), Skip を使用することで、現在のブロックの Skip 以降の処理をスキップすることが出来ます。Skip if は条件付きスキップで特定の環境でのみ Example をスキップしたいというときに使用します。, また Describe、Context、Example、Specify、It のそれぞれの頭にx をつけてxDescribe、xContext、xExample、xSpecify、xIt とすることでブロック自体を簡単にスキップすることもできます。, Pending は Skip と似ていますが機能が未実装であることを示す時に使用します。Pending は Example の実行を単にスキップする Skip とは違い Example を実行します。そして検証結果が失敗(未実装なので想定通り)であれば、スペック自体は成功となり、実装が完了し検証結果が成功となれば、スペックは失敗となり Pending を取り除くべきということがわかります。, ShellSpec 0.8.0 からの新機能です。シェルスクリプトではしばしばデータの入力を標準入力から受け取るのですがそれを簡潔に書くことができる機能です。Data を使用すると以下のように標準入力からのデータ入力を記述することが出来ます。, Data ブロックの中のコメントの行頭から #| を除いた残りの部分が標準入力データになります。Data:expand とすることで変数展開を行ったり、関数の出力結果を標準入力データとしたり Data | と書くことで別のフィルタを間に入れるといったことも可能です。, これも ShellSpec 0.8.0 からの機能です。他の DSL とは少し違う扱いなので Directive という別の名前で呼んでいます。, これらを使用して先程の Before, After のサンプルコードを書き直すと以下のようになります。, % は %const の別名で定数を定義します。ただしこの定数の値はスペックファイルの変換時に決定されます。つまり ShellSpec の環境変数である $SHELLSPEC_TMPBASE などを参照することは出来ますが、スペックファイルの変数や関数は参照できません。, もともとこの機能は、条件付きスキップで必要になりました。通常は Example を実行の起点とし、Before 等で変数の初期化などを行うのですが、 Skip if は、Example 外で処理が実行されるため、まだ変数が初期化されていないわけです。そのため内部で作成する変数ではなく外部から与えられる定数という扱いで、Describe の外でファイルレベルの定数を定義できるようにしました。これは定数であるため名前は大文字限定としています。(これは ShellCheck の警告を抑制する効果もあります。)今はただの変数として実装されているためスペックファイル内で値の変更ができるのですが将来的には readonly にするかもしれません。, そしてもう一つの Directive が 埋め込みテキストである %text です。この Directive は他の DSL と違って関数の中で使用します。%text はシェルスクリプトのヒアドキュメントの代わりとなる機能を提供します。先程の setup 関数をヒアドキュメントを使用して書くと以下のようになります。, シェルスクリプトのヒアドキュメントはインデントできないため、インデントされたコードで非常に見づらいものとなってしまいます。(正確にはハードタブを使用すればインデントは可能なのですが、ヒアドキュメントの終わりはインデントできず、ぱっと見でタブとスペースの区別がつかないためいろいろと問題があります。), 先程の例では %text の内容をリダイレクトでファイルに出力しましたが、リダイレクトを書かなければ標準出力に出力されます。Data ブロックと同様、%text:expand で変数展開を行ったり別のフィルタと組み合わせることも出来ます。, Data ブロックの例を %text を使用すると以下のように書くことが出来ます。, テストではしばしばモックやスタブを使用する必要が出てきます。現在 ShellSpec にはそのための専用の機能はありませんが、関数を一時的に置き換えることで実現できます。, get_next_day 関数は内部で date コマンドを呼び出しています。しかし直接 date コマンドを呼び出してしまうと実行するたびに値が変わるためテストになりません。そのため date コマンドを date 関数で上書きしています。これはサブシェル内の一時的な上書きであることに注意してください。そのためサブシェルを抜けると元の date コマンドが呼び出されるようになります。, この例では実際には外部コマンドを関数で上書きしており、関数を関数で上書きしたわけではありませんが、例えば unixtime 関数をブロック内で再定義してもサブシェルを抜けると元に戻ります。, Parametersでパラメータを複数定義し、テストはパラメータの部分を $1, $2, ... で置き換えるだけで、簡単にパラメータ化テストに変更することが出来ます。, こうしてみると色々と機能があるのですが、スペックファイルの文法はシンプルで自然言語に近いため覚えやすく簡単に使えると思います。その他のサンプル もこちらに用意しています。shUnit2 や Bats は実際に使ってみるとわかると思いますが、テストコードを読むのにシェルスクリプトの知識が必要になります。ShellSpec ではそれが必要最小限に抑えられているためレビューも容易になると思います。, また殆どの処理をシェルの内部コマンドだけで行っており、外部コマンドの呼び出しをほとんど行わないため極めて軽快に動作します。(テスト内容によるためはっきりとは言えませんが、shUnit2 や Bats の2~5倍以上の速度で動作するようです。2019/04/09 追記 0.10.0でサポート予定の並列実行機能でさらに2倍程度高速化します。)高速にテストを実行できることもテストツールの重要な要件の一つです。, シェルスクリプトでテストコードが必要なほど大きなものは作らないという考え方もあるかと思いますがテストは重要です。もし必要になったときは思い出してぜひ使ってみてください。, おそらくウェブアプリエンジニア。フロントやったりサーバーやったりたまにインフラ。好きなもの:シンプルで無駄のないコード、リファクタリング。嫌いなもの:技術的負債、レガシーコード. script.sh # Unit tests shUnitでテストしたいシェルスクリプトがあります。 スクリプト(とすべての関数)は、インストールがはるかに簡単になるため、単一のファイルにあります。 ASSERT:【文字が引数】Nullでない, シェルスクリプトは性質上、上から下に処理が流れてしまうため、shUnit2を使用する場合は以下の構成にするのがよいと思います。, shUnit2はブラックボックステストになるので、ブラックボックスをなるべく小さくして、それぞれを検証していくスタイルのほうがテストの堅牢性を守りつつ、ミスの少なスクリプトが作れると思います。, BASH Debugger flk_test.func に定義されている関数は3つ。, ここでそれぞれの関数の依存関係を考慮します。依存性の小さい関数からテストをすることで、依存されている側(flt_testに於いては fla_test_main)の確認観点を絞ることができるためです。, 関数名を確認観点にすると、実際にテストを実行した時の見た目で何を確認したのが分かるようになります。, assertEquals と assertNull はshUnit2で用意されているアサーション関数です。こちらは他にもいろいろと関数があるのでマニュアルを参照してもらえればと思いますが、概ねJUnitと同じ考えです。, <<アサーション失敗時のメッセージ>>はアサーションが失敗した時に標準出力に出力されるので、被疑箇所の特定がしやすくなります。, ASSERT:【文字が引数】Nullでない シェルスクリプトのユニットテスト (5) . 例えば下記のようなコードのシェルを実行したときにrmコマンドを意図的に失敗させて「失敗」を表示させるにはどうすればよいでしょうか?rmコマンドは例えなので、rmコマンドに限らず他のコマンドでも共通で意図的に失敗させることができるような方法を教えてほしいです。 #!/bin/csh -frm /et 「ごりごりの複雑なロジックだから何かが起きそう」, そうすると機能テストをする流れになると思いますが、ざっと確認してみたところ、以下の方法が考えられそう。, 今回はタイトルの通り、shUnit2について取り上げたいと思います。Batsは気になるから別の機会に。, shUnit2は前項でも書いたとおり、シェルスクリプト向け自動テストフレームワークです。 shUnit2 2.1.x nドキュメント これらの組み合わせ 16 パターンについて、テストを実行するシェルスクリプトを作成します。 Param1 が D で、Param2 が c のときの処理にバグがあって、テストが失敗するものとします。 単純に for を2重にループさせ、テストプログラムを実行することにします。 Endで終わる構文です。これは ShellSpec DSL の拡張構文で純粋なシェルスクリプトの構文ではありません。(シェルスクリプトとしてみると開始と終わりはそれぞれただの 1 命令として扱われます。), Example group のDSLには Describe と Context があり、機能的にはどちらも同じですが、テスト対象についての説明を行う時に Describe を使用し、テストの状況を説明するときには Context を使用します。これらはネスト可能で、テスト対象や状況に応じて構造化してグループ化することが出来ます。, 実行可能な例 (Example) です。このブロックの中に、何をして (Evaluation) どうあるべきか?(Expectation) を記述します。, Example group の DSL には ブロック構文の Example、Specify、It、ワンライナー構文の Todo があります。It を使って書くと説明をより自然な文章にすることが出来ますが、しっくりこない場合には、同等の機能である Example や Specify を使用することができます。Todo は実装予定の例を一行で記述するための DSLで、内容がない Example と同等の意味になります。, Example の中の When で始まる行が Evaluation です。Evaluation でテスト対象のシェル関数や外部コマンドを呼び出します。Evaluation は一つの Example の中に一つしか書けません。(省略することは出来ます。), When の次の call はシェル関数もしくは外部コマンドを呼び出すことを意味し、他に外部コマンドのみを呼び出す run やサブシェル内で呼び出す invoke (特殊な用途用なのであまり使いません)があります。一般的には call を使用すれば十分でしょう。, Example の中の The で始まる行が Expectation です。Evaluation の実行結果を検証します。Expectation は複数書くことが出来ます。複数書いた場合でも途中の失敗で停止することなくすべての Expectation が実行されます。(RSpec の aggregate_failures 相当の動作です。), should の代わりに should not を使用することで、否定の意味にすることも出来ます。, 標準出力の2行目を検証したい等といった場合に使用するのが modifier です。 modifier は subject の内容を加工し新たな subject とします。, modifier はつなげて書くことも出来ます。また数値は序数で書くことも出来ます。, subject、modifier、matcher には、標準エラー出力や終了ステータスや検証するための subject やファイルの状態や文字列が特定のパターンにマッチしているかを検証する matcher など、他にも色々あります。詳細はプロジェクトサイトを参照してください。, 以下は、Include、Before、After を使用したサンプルです。 #!/bin/sh echo $1 シェルスクリプトでも単体テストを書いてCIした方が精神衛生上好ましいなと思い、調べてみるとshUnit2という単体テストフレームワークがあったので、これを使って単体テストとCIを実施し … kcov スクリプトの作成し終えたら、単体テストをおこないます。 想定した前提条件で スクリプトを実行して、想定した結果となることを確認する「正常系」のテストと ifやwhileなどで分岐やループをしているすべての箇所が、想定した動作になっていることを確認する「異常系」のテスト … ShellSpec - シェルスクリプト用のフル機能のBDDユニットテストフレームワーク, ShellSpec は BDD スタイルのテスティングフレームワークです。他の BDD スタイルのテスティングフレームワークと同様に、自然言語に近い DSL で構造化されたテスト(実行可能な例)を記述することができます。, POSIX 互換のシェルスクリプトで実装されており、依存している外部コマンドも POSIX 準拠の基本的なものだけなので bash だけでなく dash や zsh や ksh などより多くのシェルで動作します。OS も様々なものに対応しており、Debian, macOS, Soralis, Windows(WSL)や Docker 上の Alpine Linux や組み込みで使われることが多い BusyBox でもそのまま動きます。, RSpec に大きく影響を受けており、似ている機能や設計でありながら、シェルスクリプトの用途に適した DSL となっています。, 「123 は 123である」というあまり意味がない例ですが、シェルスクリプトを知らなくとも自然言語(英語ですが・・・)として読むことが出来ます。, 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 形式に対応しています。, テスト実行の際に、行番号、ID、フォーカス、テスト名、タグ、といった様々な方法で実行するテストを指定することが出来ます。一番使用頻度が多いと思われる行番号は、(RSpecのように)以下の方法で指定することが出来ます。, またテストの、Descript や It をそれぞれ fDescribe、fIt と書き換えることでフォーカスを指定し、フォーカスされたテストだけを実行することが出来ます。これも RSpec を参考に実装したのですが、RSpecとは違い実行するときに --focus オプションが必要です。(指定しない場合はすべて実行されます), これは ShellSpec の設計上 1 パス、つまりスペックファイルを変換しながらそのまま実行しているため、最初にスペックファイルにフォーカスされた行があるか知ることが不可能だからです。--focus オプションが必要なのは制限とも思えるのですが実際のテスト修正では、エラーとなった行を修正 → フォーカスしたものだけを実行 → 全体を実行して確認 → エラーが修正されていなければその箇所のみ修正 → 繰り返す と フォーカスしたものと全体の実行を交互に行うと思うので、これはこれで良かったかなぁと思っています。, バージョン 0.10.0 で並列実行に対応しました。並列実行は Bats-core でも対応予定(おそらく1.2.0から。master には含まれています)ですが、 Bats-core では GNU parallel が必要なのに対して ShellSpec はシェルスクリプトのバックグラウンドプロセスを利用しシェルスクリプトのみで実装しています。そのためすべてのシェルで利用することが出来ます。, バージョン 0.14.0 でスペックファイルのランダム実行に対応しました。これもシェルスクリプト(とsortとodコマンド)のみで実行しています。実行順のランダム化には fnv1a で(od コマンドで 8 進数に変換した)ファイル名をハッシュ化し xorshift32 でハッシュ値からランダム値を求めてsortしています。, ハッシュを求めるコマンドは cksum (や sha1sum、md5sum等)を使用することも出来たのですが、これだとファイルの数だけコマンド呼び出しが発生するので遅くなります。また $RANDOM や /dev/urandom でランダム値を取得することも可能ですが、シェルや OS に依存します。そのためハッシュ化とランダム値の計算をシェルスクリプトで実装しました。どちらも軽量なアルゴリズムのためシェルスクリプトで実装しても特に遅さは感じません。, バージョン 0.16.0 でkcov を統合させることにより、カバレッジ機能に対応させました。もちろんカバレッジレポートも出力できるのでテストを行っていない箇所を可視化することが出来ます。, どのテスティングフレームワークにも言えることですがカバレッジ機能がフレームワークに統合されていない場合、それを自分で統合させるのはけっこう大変な作業です。ShellSpec では設計上「Kcov 上で ShellSpecを実行」するだけではカバレッジを取ることが出来ないため、ShellSpec に統合させました。, なおカバレッジを取ることが出来るシェルは bash のみです。(Kcov は bash のみに対応しており、Kcov は bash のデバッグ機能に依存しています。)これが今の所唯一の特定のシェルでしか使えない機能となります。 bash, zsh, ksh です。Kcov は bash しか対応していませんが、bash と同様の出力を行うことで、zsh と ksh に対応させました。(2020/05/11), バージョン 0.18.0 でプロファイラに対応しています。時間がかかっているテストをリストアップすることができます。が、はっきり言ってこれは一番意味がない機能だと思っています(笑), アプリケーションのプロファイラならともかく、テストのプロファイラにどういう意味があるのでしょうか?時間がかかっているテストだから短くしようという意味がなくはないですが、テストで時間がかかっているからと言って、実際のアプリケーションではボトルネックになっていなかったり、逆にテストでは短い時間でも実際のアプリケーションでは何度も呼び出すためボトルネックになるということがあるのでテストでプロファイラを使用してもあまり意味がないと思っています。, ではなぜ実装したのか?ですが、jUnit XML で(必須ではないですが)テスト実行時間の項目があるからです。というのは建前で、面白い実装方法を思いつたからです。, 多くの環境をサポートしようとするとシェルスクリプトでミリ秒の取得ができないという制限にぶち当たります。date コマンドは POSIX の範囲ではミリ秒の取得ができません。POSIX の範囲に限定するとシェルスクリプトでミリ秒を取得できるコマンドは time と times のみです。このうち times は user 時間と sys 時間は取得できますが肝心の real 時間が取得できません。そのため使えるコマンドは time のみです。しかし time は呼び出したコマンドの実行時間を調べるものなので、テスト一つごとに外部プロセスとして呼び出さなければいけません。テストでシェル関数呼び出しなどを行うのでそれは不可能です。, そこでとった手段が、プロファイリングのために数値をカウントするだけのプロセスをバックグラウンド実行させるという方法です。テスト全体の実行時間は time でわかるので、あとはテスト 1 件の開始と終了のタイミング(=カウント)がわかれば計算できます。テストの開始と終了の時点で、プロファイラプロセスに通知してその時のカウント値を記録させています。(最初はシグナルを用いて通知していましたが扱いが大変だったのでファイルを使用しています。), つまり CPU コア一つを全力でぶん回すというかなり強引な手段です。テストの実行時間に影響があるのでは?とか精度が悪いのでは?という懸念はありましたがそれなりに上手く言っているようです。(あまり意味がない機能だし・・・), バージョン 0.19.0でシングルスクリプトファイルのテストに対応しました。多くのシェルスクリプトは1ファイルの実行可能なスクリプトとして作成されていることが多いと思います。関数だけで定義されているライブラリファイルを読み込む場合は ShellSpec の Include で簡単に読み込んでテストできるのですが、実行可能なスクリプトファイルになっている場合テストが困難でした。, バージョン 0.19.0で対応した機能により、シェルスクリプトに Interception point を入れておくことでその場所で処理を割り込ませたり、特定の行でスクリプトを中断させる(Sourced Return)ことで、実行可能なスクリプトファイルのテストを可能にしました。, バージョン 0.20.0 でパラメータ化テストに対応しました。jUnit5 や rspec-parameterized にあるような機能でテストに対してパラメータを定義することで同様のテストをパラメータを変えて実行する機能です。, 実は当初の脳内設計には含まれていなくて、あとから追加したものなのですが、既存のテストのパラメータを定義するだけで簡単にパラメータ化テストに変更できるというシンプルな使い勝手を実現できたのでかなり気に入っています。(DSL はページの下の方を参照), github からコードを clone し、PATHが通った場所にシンボリックリンクを作成するだけです。, 通常はテスト対象のプロジェクトディレクトリがあると思うので、そのディレクトリ(なければ新規に作成してください)で shellspec --init を実行してください。スペックファイルを格納する spec ディレクトリと設定用のファイルが作成されます。, (とは言ってもこれらのファイルは必須ではなく、適当な場所にスペックファイルを作成して、shellspec に渡せば実行できるのですが・・・), あとは、spec ディレクトリの中にスペックファイルを作成していき、プロジェクトディレクトリ直下で shellspec を実行すれば、スペックファイルが実行されます。(スペックファイルの名前の末尾は _spec.sh である必要があります。), ShellSpec の DSL は大きく、Example group、Example、Evaluation、Expectation に分かれています。これらにあてはまらないものとして Helper と Directive があります。, BDD では一つのテストのことを実行可能な例(Example) と呼び、ShellSpec もそれに倣っています。, Example group は Example をグループ化するためのブロック構文です。ブロック構文とは特定のキーワードで始まり
ザナルカンドにて 楽譜 原曲,
筋肉痛 治らない 老化,
Ff14 ウィンドウモード 枠,
ニテル サイト 安全,
賭ケグルイ 会長 夢子,
グランツーリスモ 4 フォーミュラ,
上田市 コロナ ツイッター,
サボン スクラブ 開け方 スプーン以外,
1 8いこうよ 見逃し,
24 シーズン4 テロリスト 女,
ヒゲダン 好きな人 特徴,
エニアグラム 相性 同性,
Premium Style ワイヤレスイヤホン ペアリング,
フリーダイヤル いつでも1番おこまりに
0120-110502

神奈川県横浜市戸塚区小雀町1959-1

神奈川県横浜市青葉区みたけ台5-7