- 追加された行はこの色です。
- 削除された行はこの色です。
#author("2020-02-11T16:44:53+09:00","default:haikikyou","haikikyou")
#author("2020-02-11T19:53:23+09:00","default:haikikyou","haikikyou")
[[moritetuのIT関連技術メモ]]
#contents
* bats [#mee50b91]
unixアプリケーション
Bashで書かれたテストツールで、unixプログラムの振る舞いをチェックするために有用である。~
コマンドラインから実行できるプログラムの動作確認として幅広く使うことができる。~
何といっても非常に小さなプログラムで軽量であるのが特徴である。~
Bashなので、大抵のUnix/Linux環境で何も特別なライブラリを必要とせず導入できる。
* インストール [#g8a0fc67]
非常に簡単で、&code(){install.sh};を実行するのみである。~
&code(){install.sh};の引数で指定された&code(){PREFIX};にbatsがインストールされる。~
他manもインストールされるので、&code(){man bats};でマニュアルを確認できる。
#geshi(bash){{{
$ git clone https://github.com/sstephenson/bats.git
$ bash ./bats/install.sh /usr/local
Installed Bats to /usr/local/bin/bats
$ bats
Bats 0.4.0
Usage: bats [-c] [-p | -t] <test> [<test> ...]
}}}
* テスト [#t9a147b0]
bats自体のテストがbatsで書かれているので、お手本はbatsのテストプログラムを見ればよい。
https://github.com/sstephenson/bats/tree/master/test
** テストプログラム [#t1521afa]
-テストはBashで記述する。ただ、batsはテスト実行前にプリプロセスが入る。プリプロセスの中で、bats独自形式のシンタックスをBashで実行可能な形式に変換するとともに、テスト実行のための準備を行なっている。
- テストファイルの拡張子は、&code(){.bats};である。&code(){.bats};でなくとも単一ファイルであれば実行できるが、test suite(複数のファイルをまとめて実行)では、&code(){.bats};であることを期待している。~
よって、ファイルの拡張子は&code(){.bats};で作成しておけばよい。
&label(sample){例1}; ''sample.bats''
#geshi(bash){{{
#!/usr/bin/env bats
# This is a sample test program with bats.
@test "Here is the title of the test" {
# This is a test program
run echo "hoge"
[ $status -eq 0 ]
}
}}}
実行してみよう。
#geshi(bash){{{
$ bats sample.bats
✓ Here is the title of the test
1 test, 0 failures
}}}
** テストスイートの実行 [#d62bdc80]
batsコマンドにディレクトリを指定すると、ディレクトリ下の.batsファイルを順に実行してくれる。~
テストは、glob(&code(){*.bats};)にマッチするファイルが対象であり、実行順は特に意識されない。~
また、検索対象は指定されたディレクトリ階層下であり、2階層以上にのファイルは対象ではない。
#geshi(bash){{{
$ tree tests
tests
├── bar.bats
└── foo.bats
0 directories, 2 files
$ bats tests
✓ test2
✓ test1
2 tests, 0 failures
}}}
* 実行環境 [#x2d58e4d]
** setup、teardown [#c14645e8]
各テスト実行前に&code(){setup};、テスト終了時に&code(){teardown};を実行できる。~
プリプロセスで変換されたテスト関数を見ると、テストプログラムの前にsetup関数が呼ばれることがわかる(&code(){bats_test_begin};)。&code(){teardown};は、exit trapで実行される。
&label(sample){例}; ''setup, teardownの例''
#geshi(bash){{{
#!/usr/bin/env bats
setup() {
TEST_DATA="test"
}
teardown() {
: nothing
}
@test "test2" {
[ "$TEST_DATA" = "test" ]
}
@test "test3" {
[ "$TEST_DATA" = "test" ]
}
}}}
実行結果は以下のとおり。
#geshi(bash){{{
$ bats bar.bats
✓ test2
✓ test3
2 tests, 0 failures
}}}
** ヘルパー [#z2a5bcb8]
必要な機能やデータは、&code(){load};コマンドでインクルードできる。~
これは、&code(){source};コマンドのラッパーである。~
ファイルパスは、絶対パスまたはテスト対象ファイルからの相対パスである。
インクルードするファイルは、拡張子が&code(){.bash};であることを期待している。
#geshi(bash){{{
$ cat my.bash
ENVIRONMENT=dev
$ cat helper.bats
load my
@test "Load test" {
[ "$ENVIRONMENT" = "dev" ]
}
$ bats helper.bats
✓ Load test
1 test, 0 failures
}}}
** テスト内で使用できる変数 [#p4ad51c3]
&code(){run};コマンドで実行したコマンドの内容は、以下の変数で参照できる。
|~変数名|~説明|h
|output|標準出力、標準エラー出力(標準出力にリダイレクトされる)|
|lines|outputの\n区切りの配列|
|status|終了ステータス|
** その他 [#e41af78c]
テスト時のカレントディレクトリは、テスト実行した&code(){$PWD};である。~
ファイルを作成したり、&code(){rm};したりする場合は注意して意識して行なうのが良い。
* プリプロセス [#r3a65fb9]
batsは、テスト実行前に一度変換プロセスを挟むと前章で説明した。~
具体的にどのようなファイルが作成されるか、参考までに以下で見てみる。
プリプロセスを実行している&code(){bats-preprocess};を実行してみよう。~
先ほどの例1のsample.batsをプリプロセスにかけると以下のように変換されていることが分かる。~
現在の実行コンテキストがどうなっているのか、以下の結果から判断することができる。~
例えば、&code(){@test};の内部やファイルの内部で定義した変数の可視範囲、影響範囲は?といった内容である。
&label(sample){例}; ''sample.batsのプリプロセスの結果''
#geshi(bash){{{
$ cat sample.bats | bash /usr/local/libexec/bats-preprocess
#!/usr/bin/env bats
# This is a sample test program with bats.
test_Here_is_the_title_of_the_test() { bats_test_begin "Here is the title of the test" 5;
# This is a test program
run echo "hoge"
[ $status -eq 0 ]
}
bats_test_function test_Here_is_the_title_of_the_test
}}}
&code(){bats_test_function};は、引数で指定される関数をテスト関数として登録する。~
また、このファイルは別プロセス(サブシェル)で関数実行毎に&code(){source};コマンドでインクルードされる。~
よって、同一ファイルの複数のテストに渡って引き継ぎしたい変数は、テストファイルの&code(){@test { }}; の外のコンテキストで定義しておけばよい。~
そして、&code(){@test {}}; の中で実行される内容はサブシェルで実行されるため、あるテスト関数内で定義した変数は、他のテスト関数に影響しない。
&label(sample){例2}; ''sample2.bats''
#geshi(bash){{{
#!/usr/bin/env bats
# This is a sample test program with bats.
GLOBAL="hoge"
@test "Test1" {
[ "$GLOBAL" = "hoge" ]
export TEST1_ENV="TEST1"
}
@test "Test2" {
[ "$TEST1_ENV" = "TEST1" ]
}
}}}
実行した結果は、以下のとおりである。
#geshi(bash){{{
$ bats sample2.bats
✓ Test1
✗ Test2
(in test file sample2.bats, line 14)
`[ "$TEST1_ENV" = "TEST1" ]' failed
2 tests, 1 failure
}}}
&label(memo){メモ}; ''ソースファイルはN+1回実行される?''
sample2.batsのグローバルコンテキスト(ここでは、@testブロックの外のこととする)に&code(){echo "included"};を仕掛けて実行した場合、以下のようになる。~
&code(){included};という文字が3回出力されている。テスト数+1回評価されていることが分かる。
#geshi(bash){{{
included
1..2
included
begin 1 Test1
ok 1 Test1
included
begin 2 Test2
not ok 2 Test2
# (in test file sample2.bats, line 14)
# `[ "$TEST1_ENV" = "TEST1" ]' failed
}}}
* 関連 [#s41a2e35]
- https://github.com/moritetu/baut
* 参考リンク [#de538270]
- [[bats>https://github.com/sstephenson/bats]]- &size(11){&color(gray){on https://github.com/sstephenson/bats};};