本文分析了bats--Bash自動化測試工具的安裝、語法、常用指令及常用函式等內容。
上篇文章回顧:學習RAID 01/10/10E的區別
bats 是一個符合 TAP 標準 的 Bash 版測試框架,它使用了一種極為簡便的方法來驗證命令列程式是否正常執行。
bats 要求 Bash 的最低版本是 3.2.57 ,bats 測試檔案實際上一個 bash 的指令碼檔案,完全可以使用 shell 的語法書寫。
一、安裝
強烈建議使用原始碼或者npm安裝最新版本, bats 經過多人接手,程式碼存放比較混亂,某些系統下安裝的是舊版本的,目前社群在維護的版本地址為:
https://github.com/bats-core/bats-core.git
01macOS下安裝
brew install bats-core複製程式碼
02CentOS下安裝
yum install bats複製程式碼
03UBuntu下安裝
apt-get install bats複製程式碼
04Windows安裝
Git for Windows Bash (MSYS2 based)
Windows Subsystem for Linux
MSYS2
Cygwin
Windows下推薦使用原始碼安裝或者 npm 的方式進行安裝。
05使用原始碼安裝
git clone https://github.com/bats-core/bats-core.gitcd bats-core
./install.sh /usr/local
複製程式碼
06使用Docker安裝
docker pull bats/bats
docker run -it --rm bats/bats --version
docker run -it --rm -v "$(pwd):/code" bats/bats /code/test複製程式碼
07使用npm安裝
npm install -g bats
複製程式碼
經過測試 CentOS 下的版本為 2014 年的版本,ubuntu下也不是最新版本,建議使用原始碼或者npm安裝最新版本。
二、語法
測試用例的語法格式為:
#!/usr/bin/env bats@test "grep --version check" { # 測試用例名稱
run grep --version # 執行的外部命令
[ $status -eq 0 ] # 斷言
[ "${lines[0]%% *}" == 'grep' ] # 斷言
}複製程式碼
每個 Bats 測試檔案的評估次數為 n + 1 次,其中 n 是檔案中的測試用例數。執行測試指令碼時首先計算測試用例的數量,然後遍歷測試用例並在獨立程式中執行每個測試用例。
在執行測試用例時,Bats使用Bash的 errexit(set -e)選項,這樣寫在@test 裡面的語句都是真理斷言。一旦測試用例中的某一個斷言失敗(某條語句的狀態碼不是 0)則這個測試用例視為失敗。
三、常用指令
01run:執行外部命令
用於測試外部命令然後對它的退出狀態和輸出狀態進行斷言。Bats 包含一個 run 的指令,它可以將傳入的引數當成命令呼叫,並且將退出狀態和輸出狀態儲存到特殊的全域性變數中,以便可以繼續在測試用例中增加斷言。
比如說我們正在測試 cat:
#!/usr/bin/env bats@test "cat nonexistent_filename check" { # 測試用例名稱
run cat nonexistent_filename # 執行的外部命令
[ $status -eq 1 ] # 斷言
[ "$output" == 'cat: nonexistent_filename: No such file or directory' ] # 斷言
[ "${lines[0]}" == 'cat: nonexistent_filename: No such file or directory' ] # 斷言
}複製程式碼
這裡有三個 Bats 的特殊變數
$status 是命令退出狀態碼
$output 是命令的標準輸出和標準錯誤的內容
$lines是命令輸出內容的陣列包含各行內容 cat nonexistent_filename 只有一行內容
02load:使用共享檔案
如果想用跨越多個測試檔案共享環境變數或者自定義的函式,可以使用 load 指令。共享檔案的擴充套件檔名必須是.bash。load可以使用相對路徑或者絕對路徑。
使用相對路徑的寫法是(可以省略擴充套件檔名):
load test_helper複製程式碼
使用絕對路徑的寫法時(必須帶上擴充套件檔名):
load /test_helpers/test_helper.bash複製程式碼
03skip:跳過測試
在測試過程中如果失敗時如果想繼續可以 skip 指令來跳過測試:
@test "A test I don't want to execute for now" {
skip
run foo
[ "$status" -eq 0 ]
}複製程式碼
也可以加入跳過原因:
@test "A test I don't want to execute for now" {
skip "This command will return zero soon, but not now"
run foo
[ "$status" -eq 0 ]
}複製程式碼
或者也可以根據條件判斷是否跳過:
@test "A test which should run" { if [ foo != bar ]; then
skip "foo isn't bar"
fi
run foo
[ "$status" -eq 0 ]
}複製程式碼
四、常用函式
01@test
這是 Bats 主要的函式所有的測試用例都要按照這個函式的格式書寫:
@test "grep --version check" { # 測試用例名稱
run grep --version # 執行的外部命令
[ $status -eq 0 ] # 斷言
[ "${lines[0]%% *}" == 'grep' ] # 斷言
}複製程式碼
02setup/teardown初始化和善後函式
setup / teardown 是兩個特殊的函式,用於在測試用例開始之前和結束之後進行初始化和善後工作。比如開始之前設定環境變數建立測試目錄。以 soar 為測試用例為例:
setup() {
export SOAR_DEV_DIRNAME="${BATS_TEST_DIRNAME}/../"
export SOAR_BIN="${SOAR_DEV_DIRNAME}/bin/soar"
export SOAR_BIN_ENV="${SOAR_DEV_DIRNAME}/bin/soar -config ${SOAR_DEV_DIRNAME}/etc/soar.yaml"
export BATS_TMP_DIRNAME="${BATS_TEST_DIRNAME}/tmp"
export BATS_FIXTURE_DIRNAME="${BATS_TEST_DIRNAME}/fixture"
mkdir -p "${BATS_TMP_DIRNAME}"}
teardown(){ //TODO
......
}複製程式碼
五、特殊變數
Bats 中包含幾個全域性變數 :
$BATS_TEST_FILENAME Bats測試檔案的絕對路徑。
$BATS_TEST_DIRNAME Bats測試檔案所在的目錄。
$BATS_TEST_NAMES 每個測試用例的函式名稱陣列。
$BATS_TEST_NAME 包含當前測試用例的函式的名稱。
$BATS_TEST_DESCRIPTION 當前測試用例的描述。
$BATS_TEST_NUMBER 測試檔案中當前測試用例的(從1開始)索引。
$BATS_TMPDIR 用於儲存臨時檔案的目錄的位置。
六、注意事項
01寫在@test之外的程式碼
寫在 @test 函式之外程式碼一旦失敗 Bats 會立刻中斷執行,某些情況下這樣做會很有用比如檢查依賴項,但是如果在@test、setup、teardown之外列印的任何輸出必須重定向到stderr(>&2),否則這些輸出內容可能會汙染TAP流導致Bats 測試失敗。
經過測試 @test 之外的程式碼會優先執行
02檔案描述符3(FD3)
如果Bats卡死可以讀這一塊內容。
Bats 將測試程式碼的輸出流和 TAP 輸出流分開,這樣做的目的是為了確保 TAP 的輸出不被汙染。在輸出至終端的部分詳細介紹瞭如何使用 FD3 正確列印自定義文字。
但是使用 FD3 的一個已知的問題是:在某些情況下(如程式的子程式在後臺執行的時候),它會導致 Bats 卡死。在這種情況下在生成子程式的時候,子程式會從父程式繼承 FD3 ,導致Bats 會等待子程式執行完成之後關閉 FD3 。如果子程式需要花費大量時間完成,例如,如果子程式是 sleep 100 命令或是後臺服務,那麼 Bats 也會阻塞同樣的時間。
為了避免這種情況,啟動可能長時間執行的子程式之前顯式關閉 FD3 command_name 3>&-
舉例說明:
1.會卡死的情況:
@test "cat nonexistent_filename check" { # 測試用例名稱
run cat nonexistent_filename
sleep 100 & # 後臺執行
[ $status -eq 1 ]
[ "$TTTT" -eq 1 ] # 斷言
[ "$output" == 'cat: nonexistent_filename: No such file or directory' ] # 斷言
}複製程式碼
2. 不會卡死的情況:
@test "cat nonexistent_filename check" { # 測試用例名稱
run cat nonexistent_filename
sleep 100 3>&- & # 後臺執行並且關閉檔案描述符3
[ $status -eq 1 ]
[ "$TTTT" -eq 1 ] # 斷言
[ "$output" == 'cat: nonexistent_filename: No such file or directory' ] # 斷言
}複製程式碼
03命令輸出到終端
在 @test 函式內部輸出
在 setup/teardown 函式內部輸出
在 @test 或者setup/teardown 外部輸出
(1)如果從@test 內部輸出字元你需要將輸出重定向到 FD3 例如 echo 'test'>&3。這時輸出將變成 TAP 流的一部分。為了生成100%符合 TAP 流格式的輸出,我們推薦的寫法是 echo '# text' >&3 。否則,在使用分析 TAP 流的第三方工具時可能會遇到意外錯誤。
(2)Bats 預設使用友好的輸出格式(-p, --pretty)。 TAP 流預設不會從 FD3 中輸出任何字元
(3)無論指定何種輸出格式,直接輸出到 stdout、stderr 的文字(例如 echo "aaaa")會被@test 視為測試的一部分,僅僅在測試失敗的時候顯示。
這一部分內容輸出行為和@test 中一樣。
(1)無論輸出的字元被重定向到何處(FD1、FD2、FD3)字元都會立刻顯示到終端
(2)按照這種方式列印的文字將會禁用友好的輸出格式(-p, --pretty)。此外他也會使得輸出不符合 TAP 流規範。
(3)由內部管道或者重定向輸出到標準錯誤的字元總會第一時間顯示出來。
七、Bats命令列用法
bats 命令列用法:
Bats 1.1.0 Usage: bats [-cr] [-f <regex>] [-p | -t] <test>...
bats [-h | -v]
<test> 為一個 bats 測試用例的檔案,或者是一個包含字尾名為 .bats 檔案的目錄
-c, --count 計算沒有執行的測試用例的個數
-f, --filter 通過正規表示式指定執行某些測試用例
-h, --help 顯示幫助資訊
-p, --pretty 以比較友好的方式展現測試用例的輸出結果(預設是使用這種方式)
-r, --recursive 在子目錄中包含測試
-t, --tap 以 TAP 格式顯示輸出結果
-v, --version 顯示版本號資訊複製程式碼
更多資訊見:
https://github.com/bats-core/bats-core複製程式碼
八、開發工具推薦
在常用的開發工具中安裝 Bats 外掛,增加程式碼高亮顯示和程式碼完成提示,提高開發效率。
01VS Code
02Sublime Text
03Vim
九、其他開發工具
更多開發工具和外掛參考:
十、參考文獻
bats-core:
Bat Evaluation Process:
本文首發於公眾號”小米運維“,點選檢視原文。