測(cè)試用例難寫?來(lái)試試 Sharness
Sharness 是一個(gè)用 Shell 腳本來(lái)編寫測(cè)試用例的測(cè)試框架。本文將詳細(xì)介紹 Sharness 的結(jié)構(gòu)及測(cè)試用例的編寫格式,以及語(yǔ)法規(guī)范和技巧,教大家如何使用 Sharness 編寫測(cè)試用例,同時(shí)參與過(guò) Git 項(xiàng)目的測(cè)試用例開(kāi)發(fā),為其測(cè)試框架的簡(jiǎn)潔、高效而折服。曾經(jīng)嘗試將 Git 測(cè)試用例用于其他項(xiàng)目:《復(fù)用 git.git 測(cè)試框架》[1]。不過(guò)從 Git 項(xiàng)目中剝離測(cè)試用例框架還是挺費(fèi)事的。
一次偶然的機(jī)會(huì)發(fā)現(xiàn)已經(jīng)有人(Christian Couder:Gitlab 工程師,Git項(xiàng)目的領(lǐng)導(dǎo)委員會(huì)成員之一)已經(jīng)將 Git 的測(cè)試用例框架剝離出來(lái), 成為獨(dú)立的開(kāi)源項(xiàng)目 Sharness。
有了 Sharness,寫測(cè)試用例不再是苦差事。
一 Sharness 是什么?
- Sharness 是一個(gè)用 Shell 腳本來(lái)編寫測(cè)試用例的測(cè)試框架。
- 可以在 Linux、macOS 平臺(tái)運(yùn)行測(cè)試用例。
- 測(cè)試輸出符合 TAP(test anything protocol),因此可以用 sharness 自身工具或 prove 等 TAP 兼容測(cè)試夾具(harness)運(yùn)行。
- 是由Junio在2005年為Git項(xiàng)目開(kāi)發(fā)的測(cè)試框架,由 Christian Couder (chriscool) 從 Git 中剝離為獨(dú)立測(cè)試框架。
- 地址:https://github.com/chriscool/sharness
二 Sharness 測(cè)試框架的優(yōu)點(diǎn)
簡(jiǎn)潔
如果要在測(cè)試用例中創(chuàng)建/初始化一個(gè)文件(內(nèi)容為 “Hello, world.”), 看看 sharness 實(shí)現(xiàn)起來(lái)有多么簡(jiǎn)單:
如果要對(duì)某應(yīng)用(hello-world)的輸出和預(yù)期的 expect 文件進(jìn)行比較, 相同則測(cè)試用例通過(guò),不同則展示差異。測(cè)試用例編寫如下:
調(diào)試方便
每個(gè)測(cè)試用例腳本可以單獨(dú)執(zhí)行。使用 -v 參數(shù),可以顯示詳細(xì)輸出。使用 -d 參數(shù),運(yùn)行結(jié)束后保留用例的臨時(shí)目錄。
可以在要調(diào)試的test case后面增加 test_pause 語(yǔ)句,例如:
然后使用 -v 參數(shù)運(yùn)行該腳本,會(huì)在 test_pause 語(yǔ)句處中斷,進(jìn)入一個(gè)包含 sharness 環(huán)境變量的子 Shell 中,目錄會(huì)切換到測(cè)試用例單獨(dú)的工作區(qū)。調(diào)試完畢退出 Shell 即返回。
三 Git 項(xiàng)目的測(cè)試框架結(jié)構(gòu)
Sharness 源自于 Git 項(xiàng)目的測(cè)試用例框架。我們先來(lái)看看 Git 項(xiàng)目測(cè)試框架的結(jié)構(gòu)。
Git 項(xiàng)目測(cè)試相關(guān)文件
- 待測(cè)應(yīng)用放在項(xiàng)目的根目錄。例如 Git 項(xiàng)目的待測(cè)應(yīng)用: git 和 git-receive-pack 等。
- 測(cè)試框架修改 PATH 環(huán)境變量,使得測(cè)試用例在調(diào)用待測(cè)應(yīng)用(如 git 命令)的時(shí)候,優(yōu)先使用項(xiàng)目根目錄下的待測(cè)應(yīng)用。
- 測(cè)試腳本命名為 tNNNN-.sh,即以字母 t 和四位數(shù)字開(kāi)頭的腳本文件。
- 每一個(gè)測(cè)試用例在執(zhí)行時(shí)會(huì)創(chuàng)建一個(gè)獨(dú)立的臨時(shí)目錄,例如 trash directory.t5323-pack-redundant。測(cè)試用例執(zhí)行成功,則該目錄會(huì)被刪除。
相關(guān)代碼參見(jiàn)[2]。
四 Git 測(cè)試腳本的格式
以如下測(cè)試腳本為例[3]:
(1)在文件頭,定義 test_description 變量,提供測(cè)試用例的簡(jiǎn)單說(shuō)明,通常使用一行文本。本測(cè)試用例較為復(fù)雜,使用了多行文本進(jìn)行描述。
(2)包含測(cè)試框架代碼。
(3)定義全局變量,以及定義要在測(cè)試用例中用到的函數(shù)封裝。
(4)用 test_expect_success 等方法撰寫測(cè)試用例。
(5)在腳本的結(jié)尾,用 test_done 方法結(jié)束測(cè)試用例。
五 Sharness 測(cè)試框架結(jié)構(gòu)
Sharness 項(xiàng)目由 Git 項(xiàng)目的測(cè)試框架抽象而來(lái),項(xiàng)目地址:
??https://github.com/chriscool/sharness??
Sharness 測(cè)試框架示例
- 待測(cè)應(yīng)用放在項(xiàng)目的根目錄。
- 測(cè)試腳本命名為 .t,即擴(kuò)展名為 .t 的腳本文件。
- 每一個(gè)測(cè)試用例在執(zhí)行時(shí)會(huì)創(chuàng)建一個(gè)獨(dú)立的臨時(shí)目錄,例如 trash directory.simple.t。測(cè)試用例執(zhí)行成功,則該目錄會(huì)被刪除。
- 在 sharness.d 目錄下添加自定義腳本,可以擴(kuò)展 Sharness 框架。即在框架代碼加載時(shí),自動(dòng)加載該目錄下文件。
我們對(duì) Sharness 測(cè)試框架做了一些小改動(dòng):
- 定制版本對(duì)測(cè)試框架代碼做了進(jìn)一步封裝,框架代碼放在單獨(dú)的子目錄。
- 測(cè)試腳本的名稱恢復(fù)為和 Git 項(xiàng)目測(cè)試腳本類似的名稱(tNNNN-.sh), 即以字母 t 和四位數(shù)字開(kāi)頭的腳本文件。
定制版 Sharness 測(cè)試框架示例
六 Sharness 測(cè)試用例格式
以如下測(cè)試腳本為例[4]:
(1)在文件頭,定義 test_description 變量,提供測(cè)試用例的簡(jiǎn)單說(shuō)明,通常使用一行文本。
(2)包含測(cè)試框架代碼。
(3)定義全局變量,以及定義要在測(cè)試用例中用到的函數(shù)封裝。
(4)用 test_expect_success 等方法撰寫測(cè)試用例。
(5)在腳本的結(jié)尾,用 test_done 方法結(jié)束測(cè)試用例。
七 關(guān)于 test_expect_success 方法的參數(shù)
test_expect_success 可以有兩個(gè)參數(shù)或者三個(gè)參數(shù)。
當(dāng) test_expect_success 方法后面是兩個(gè)參數(shù)時(shí),第一個(gè)參數(shù)用于描述測(cè)試用例, 第二個(gè)參數(shù)是測(cè)試用例要執(zhí)行的命令。
當(dāng) test_expect_success 方法后面是三個(gè)參數(shù)時(shí),第一個(gè)參數(shù)是前置條件, 第二個(gè)參數(shù)用于描述測(cè)試用例, 第三個(gè)參數(shù)是測(cè)試用例要執(zhí)行的命令。
例如如下有三個(gè)參數(shù)的測(cè)試,第一個(gè)參數(shù)定義了前置條件,在 CYGWIN 等環(huán)境, 不執(zhí)行測(cè)試用例。
八 Sharness 語(yǔ)法規(guī)范和技巧
使用 && 級(jí)聯(lián)各個(gè)命令,確保所有命令都全部執(zhí)行成功
自定義方法,也要使用 && 級(jí)聯(lián),確保命令全部執(zhí)行成功
涉及到目錄切換,在子 Shell 中進(jìn)行,以免影響后續(xù)測(cè)試用例執(zhí)行時(shí)的工作目錄
函數(shù)命名要有意義
如下內(nèi)容是 Junio 在代碼評(píng)審時(shí),對(duì)測(cè)試用例中定義的 format_git_output 方法的評(píng)審意見(jiàn)。Junio 指出要在給函數(shù)命名時(shí),要使用更有意義的名稱。
Heredoc 的小技巧
使用 heredoc 創(chuàng)建文本文件,如果其中的腳本要定義和使用變量,要對(duì)變量中的 $ 字符進(jìn)行轉(zhuǎn)移。Junio 給出了一個(gè) heredoc 語(yǔ)法的小技巧,可以無(wú)需對(duì) $ 字符轉(zhuǎn)義。
Shell 編程語(yǔ)法規(guī)范
Git 項(xiàng)目對(duì)于 Shell 編寫的測(cè)試用例制定了語(yǔ)法規(guī)范,例如:
- 使用 tab 縮進(jìn)。
- 規(guī)定 case 語(yǔ)句、if 語(yǔ)句的縮進(jìn)格式。
- 輸入輸出重定向字符后面不要有空格。
- 使用 $(command) 而不是 `command` 。
- 使用 test 方法,不要使用 [ ... ] 。
完整語(yǔ)法規(guī)范參考[5]。
九 sharness 常見(jiàn)的內(nèi)置函數(shù)
- test_expect_success
開(kāi)始一個(gè)測(cè)試用例。
- test_expect_failure
標(biāo)記為存在已知問(wèn)題,執(zhí)行失敗不報(bào)錯(cuò),執(zhí)行成功則警告該 broken 的用例已經(jīng) fixed。
- test_must_fail
后面的一條命令必須失敗。如果后面命令成功,測(cè)試失敗。
- test_expect_code
命令以給定返回值結(jié)束。
- test_cmp
比較兩個(gè)文件內(nèi)容,相同成功,不同失敗并顯示差異。
- test_path_is_file
參數(shù)必須是一個(gè)文件,且存在。
- test_path_is_dir
參數(shù)必須是一個(gè)目錄,且存在。
- test_must_be_empty
參數(shù)指向的文件內(nèi)容必須為空。
- test_seq
跨平臺(tái)的 seq,用戶生成數(shù)字序列。
- test_pause
測(cè)試暫停,進(jìn)入子 Shell。
- test_done
測(cè)試用例結(jié)束。
十 擴(kuò)展 Sharness
Sharness 提供了擴(kuò)展功能。用戶在 sharness.d 目錄中添加以 .sh 結(jié)尾的腳本文件,即可對(duì) Sharness 進(jìn)行擴(kuò)展。例如:
- 在 trash directory.* 目錄下執(zhí)行 git init 命令。目的是避免目錄逃逸時(shí)誤執(zhí)行 git 命令影響項(xiàng)目本身代碼。
例如:測(cè)試腳本在工作區(qū)下創(chuàng)建了一個(gè)倉(cāng)庫(kù)(git init my.repo),想要在該倉(cāng)庫(kù)下執(zhí)行 git clean,卻忘了進(jìn)入到 my.repo 子目錄再執(zhí)行,結(jié)果導(dǎo)致待測(cè)試項(xiàng)目中丟失文件。
- 引入 Git 項(xiàng)目中的一些有用的測(cè)試方法。
如:test_tick 方法,可以設(shè)置 GIT_AUTHOR_DATE、GIT_COMMITTER_DATE 等環(huán)境變量,確保測(cè)試腳本多次運(yùn)行時(shí)提交時(shí)間的一致性,進(jìn)而產(chǎn)生一致的提交ID。
- 引入項(xiàng)目需要的其他自定義方法。
例如 git-repo 項(xiàng)目為了避免工作區(qū)逃逸,在 trash directory.* 目錄下創(chuàng)建 .repo 文件。
十一 Sharness 在項(xiàng)目中的實(shí)戰(zhàn)
git-repo 是一個(gè)命令行工具,非常適合使用 sharness 測(cè)試框架編寫測(cè)試用例。參見(jiàn)[6]。
對(duì)于非命令行工具,或者為了測(cè)試內(nèi)置函數(shù),需要先封裝一個(gè)或多個(gè) fake app,再調(diào)用封裝的命令行工具進(jìn)行測(cè)試。例如在為 Git 項(xiàng)目開(kāi)發(fā) proc-receive 鉤子擴(kuò)展時(shí),先開(kāi)發(fā)一個(gè) fake app[7]。
之后再編寫測(cè)試,調(diào)用 fake app(test-tool proc-receive),幫助完成測(cè)試用例的開(kāi)發(fā)。參見(jiàn)下列提交中的測(cè)試用例[8]。
還可以使用一些 Shell 編程技巧,在多個(gè)測(cè)試文件中復(fù)用測(cè)試用例。例如如下測(cè)試用例在測(cè)試 HTTP 協(xié)議和本地協(xié)議時(shí),復(fù)用了同一套測(cè)試用例(t5411目錄下的測(cè)試腳本)[9]。
相關(guān)鏈接
[1]https://www.worldhello.net/2013/10/26/test-gistore-using-git-test-framework.html
[2]https://sourcegraph.com/github.com/git/git@master/-/tree/t
[3]https://github.com/git/git/blob/master/t/t5323-pack-redundant.sh
[4]https://github.com/alibaba/git-repo-go/blob/master/test/t0100-init.sh
[5]https://github.com/git/git/blob/master/Documentation/CodingGuidelines
[6]https://github.com/alibaba/git-repo-go
[7]https://github.com/jiangxin/git/blob/jx/proc-receive-hook/t/helper/test-proc-receive.c
[8]https://github.com/jiangxin/git/commit/9654f5eda1153634ab09ca5c6e490bcabdd57e61
[9]https://github.com/jiangxin/git/blob/jx/proc-receive-hook/t/t5411-proc-receive-hook.sh還分享了 Sharness 的擴(kuò)展功能和項(xiàng)目實(shí)戰(zhàn)。
【本文為51CTO專欄作者“阿里巴巴官方技術(shù)”原創(chuàng)稿件,轉(zhuǎn)載請(qǐng)聯(lián)系原作者】