言u(píng)nittest的魅力:flint的Test.d單元測(cè)試策略完整解析)
D語(yǔ)言u(píng)nittest的魅力flint的Test.d單元測(cè)試策略完整解析【免費(fèi)下載鏈接】flintAn open-source lint program for C developed by, and formerly used at Facebook.項(xiàng)目地址: https://gitcode.com/gh_mirrors/fli/flintflint 是一個(gè)由 Facebook 開發(fā)并實(shí)際生產(chǎn)使用的 C lint靜態(tài)代碼檢查工具其主版本完全用D語(yǔ)言編寫。而項(xiàng)目的單元測(cè)試核心就濃縮在一個(gè)文件里Test.d—— 它利用 D 語(yǔ)言原生的unittest測(cè)試塊覆蓋了詞法分析器Tokenizer與 39 條 lint 規(guī)則堪稱開源項(xiàng)目中少見的單元測(cè)試范本。一、flint 是什么用 D 語(yǔ)言寫的 C 代碼檢查器flint 的工作流程很直白讀取 C/C 源碼 → 切分成 token → 逐條 lint 規(guī)則掃描 → 輸出告警。項(xiàng)目的主要模塊分工如下文件職責(zé)Tokenizer.d把 C 源碼切分為 token 流詞法分析Checks.d39 個(gè)checkXxx函數(shù)即全部 lint 規(guī)則實(shí)現(xiàn)FileCategories.d按擴(kuò)展名識(shí)別頭文件 / C 源碼 / C 源碼Main.d程序入口負(fù)責(zé)文件遍歷與調(diào)度Test.d單元測(cè)試總?cè)肟诠?50 個(gè)unittest測(cè)試塊值得注意的是flint 同時(shí)保留了早期 C 版實(shí)現(xiàn)見TARGETS中的test-cxx目標(biāo)方便歷史對(duì)比而 D 版才是官方支持的主力這也正是本文的主角。二、D語(yǔ)言u(píng)nittest機(jī)制測(cè)試塊與源碼同處一個(gè)模塊D 語(yǔ)言的unittest { }塊直接寫在模塊里隨模塊一起編譯——只有構(gòu)建測(cè)試目標(biāo)時(shí)才會(huì)啟用。flint 在Makefile.am中把這一點(diǎn)玩得很簡(jiǎn)單正式產(chǎn)品flint編譯Main.d 四個(gè)核心模塊測(cè)試產(chǎn)品flint_test把Test.d和同一批核心模塊一起交給 D 編譯器gdc/dmd/ldc由configure.ac自動(dòng)探測(cè)編譯產(chǎn)出。這意味著測(cè)試和實(shí)現(xiàn)天然共享同一份代碼無需任何 mock、無構(gòu)建腳本魔法一行編譯命令即可完成。對(duì)新手來說這是理解單元測(cè)試最小成本的最好例子。三、Test.d 的三層測(cè)試策略解析第一層Tokenizer 詞法層測(cè)試測(cè)試塊直接內(nèi)嵌 C 代碼字符串調(diào)用tokenize后斷言 token 數(shù)量與類型。例如針對(duì)數(shù)字字面量的用例unittest { string s .123 123.234e345 0x1.fffffffffffffp1023 ; Token[] tokens tokenize(s, nofile.cpp); foreach (ref t; tokens[0 .. $ - 1]) { assert(t.type_ tk!number); } }這一層還覆蓋了極易出錯(cuò)的邊界多行單行注釋反斜杠續(xù)行、#pragma/#error預(yù)處理指令、原始字符串R(...)甚至用一張 60 余行的見證表逐一核對(duì)全部 C 運(yùn)算符 token——保證詞法層一個(gè)符號(hào)都不漏。第二層lint 規(guī)則回歸測(cè)試這是 50 個(gè)測(cè)試塊的大頭策略是一小段可疑代碼 精確的告警計(jì)數(shù)。以構(gòu)造函數(shù)缺少 explicit規(guī)則為例code class AA { AA(int bad); }; ; tokens tokenize(code); assert(checkConstructors(nofile.cpp, tokens) 3);每個(gè)規(guī)則都會(huì)同時(shí)覆蓋正例應(yīng)告警與白名單例外explicit標(biāo)記、/* implicit */注釋豁免、 delete的移動(dòng)構(gòu)造等一個(gè)都不放過。這種正反成對(duì)的寫法讓每條規(guī)則的行為邊界一目了然。第三層真實(shí)文件與二進(jìn)制集成測(cè)試真實(shí)文件一個(gè)測(cè)試塊會(huì)讀取倉(cāng)庫(kù)里的 test_files/Test1.cpp 實(shí)體文件驗(yàn)證checkDirectStdInclude在非玩具代碼上仍精確輸出 3 條告警二進(jìn)制集成testCxxReplace塊用executeShell實(shí)際運(yùn)行_bin/linters/flint/cxx_replace可執(zhí)行文件檢查文件內(nèi)容是否被正確替換測(cè)試完還自動(dòng)清理臨時(shí)文件全局狀態(tài)守衛(wèi)Test.d末尾的main里有一句enforce(c_mode false)防止某個(gè)測(cè)試塊污染了全局c_mode開關(guān)而無人察覺——這是很少有人想到的收尾檢查。四、三個(gè)值得抄走的測(cè)試技巧自定義 EXPECT_EQ 斷言Test.d內(nèi)定義了一個(gè)小工具失敗時(shí)自動(dòng)打印文件:行號(hào) 實(shí)際值 ! 期望值比裸assert的報(bào)錯(cuò)信息友好得多測(cè)試數(shù)據(jù)即字符串所有 C 樣例都用 D 的三重引號(hào)字符串q[...]、反引號(hào)字面量?jī)?nèi)嵌測(cè)試完全自包含無外部數(shù)據(jù)文件依賴改一處跑一處條件編譯分流version(facebook) { unittest { ... } }讓內(nèi)部環(huán)境專屬的規(guī)則測(cè)試只在 Facebook 內(nèi)部構(gòu)建時(shí)參與編譯開源構(gòu)建零負(fù)擔(dān)。五、快速上手如何運(yùn)行 flint 的單元測(cè)試準(zhǔn)備依賴gdc或 dmd/ldc、automake、autoconf、libtool 等configure.ac會(huì)自動(dòng)探測(cè) D 編譯器按 README.md 執(zhí)行autoreconf --install、./configure、make構(gòu)建產(chǎn)物flint_test即單元測(cè)試可執(zhí)行文件flint為正式工具。六、參考資料測(cè)試總?cè)肟赥est.d50 個(gè) unittest 塊2600 行l(wèi)int 規(guī)則實(shí)現(xiàn)Checks.d39 個(gè) check 函數(shù)詞法分析Tokenizer.d構(gòu)建配置Makefile.am、configure.ac內(nèi)網(wǎng)測(cè)試目標(biāo)定義TARGETSd_unittest與cpp_unittest雙軌總結(jié)flint 的Test.d證明了單元測(cè)試不必依賴重型框架——靠 D 語(yǔ)言u(píng)nittest塊 字符串測(cè)試數(shù)據(jù) 精確計(jì)數(shù)斷言就能為整個(gè) lint 引擎建立起高置信度的回歸網(wǎng)。對(duì)想給自家項(xiàng)目做輕量但扎實(shí)測(cè)試的同學(xué)這套策略非常值得借鑒。【免費(fèi)下載鏈接】flintAn open-source lint program for C developed by, and formerly used at Facebook.項(xiàng)目地址: https://gitcode.com/gh_mirrors/fli/flint創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考