戰(zhàn):打造接口測(cè)試通用校驗(yàn)?zāi)0鍘? alt=)
1. 只看狀態(tài)碼的校驗(yàn)漏洞為什么 Postman 斷言才是接口測(cè)試真正的核心我做過幾年接口測(cè)試見過太多人把 Postman 當(dāng)成高級(jí)瀏覽器發(fā)一個(gè)請(qǐng)求看到狀態(tài)碼 200截圖完事??雌饋頊y(cè)試報(bào)告里一切都綠實(shí)際上接口返回體里是status: fail還是status: success機(jī)器根本沒有替你判斷過。Postman 斷言也就是 Tests 標(biāo)簽頁里的腳本就是用來解決這個(gè)問題的——讓請(qǐng)求返回之后自動(dòng)執(zhí)行一套校驗(yàn)邏輯把狀態(tài)碼、響應(yīng)頭、JSON 結(jié)構(gòu)、字段值全部驗(yàn)一遍錯(cuò)在哪一步直接紅燈。1.1 狀態(tài)碼 200 不等于接口正確一個(gè)極容易漏掉的例子先看一個(gè)真實(shí)項(xiàng)目里非常常見的場(chǎng)景。登錄接口輸入了正確的賬號(hào)但故意配錯(cuò)驗(yàn)證碼服務(wù)端返回的 HTTP 狀態(tài)碼依然是 200可響應(yīng)體是這樣的{ status: fail, code: 10001, message: 驗(yàn)證碼錯(cuò)誤, data: null }如果你只做“狀態(tài)碼 200”級(jí)別的校驗(yàn)這種用例跑一百遍都是綠的因?yàn)檎?qǐng)求在傳輸層確實(shí)成功了200 只代表服務(wù)端接收并處理了這個(gè)請(qǐng)求并不代表業(yè)務(wù)邏輯正確。分頁接口沒有數(shù)據(jù)返回 200、權(quán)限不足返回 200、數(shù)據(jù)重復(fù)返回 200這些都是接口測(cè)試?yán)镒钊菀茁┑舻膯栴}。斷言的本質(zhì)就是把“人眼來判斷返回內(nèi)容”這件事交給機(jī)器。狀態(tài)碼看的是請(qǐng)求有沒有被正確處理JSON 結(jié)構(gòu)看的是返回的數(shù)據(jù)長什么樣字段值看的是業(yè)務(wù)邏輯是否真的成立。三層都覆蓋到了接口測(cè)試才真正有意義。1.2 三層校驗(yàn)狀態(tài)層、結(jié)構(gòu)層、業(yè)務(wù)層在做模板庫之前先把校驗(yàn)維度拆清楚。我一般把接口校驗(yàn)分成三層狀態(tài)層HTTP 狀態(tài)碼、響應(yīng)時(shí)間、響應(yīng)頭。這一層回答“請(qǐng)求有沒有被正確處理”。結(jié)構(gòu)層JSON 是否有對(duì)應(yīng)字段、字段類型是否正確、數(shù)組長度是否符合預(yù)期。這一層回答“返回的數(shù)據(jù)長什么樣”。業(yè)務(wù)層字段值是否合理、業(yè)務(wù)規(guī)則是否成立、多個(gè)接口之間的數(shù)據(jù)是否聯(lián)動(dòng)。這一層回答“功能到底對(duì)不對(duì)”。很多團(tuán)隊(duì)其實(shí)只做了第一層第二層看心情寫第三層基本靠人工抽查。當(dāng)你準(zhǔn)備建一套通用接口校驗(yàn)?zāi)0鍘斓臅r(shí)候核心工作就是把這三層固化成可復(fù)用的腳本塊讓每個(gè)接口請(qǐng)求跑完以后自動(dòng)執(zhí)行一次“體檢”。這件事一旦做起來測(cè)試效率的提升是肉眼可見的尤其到了回歸階段幾百個(gè)接口不可能靠人眼一個(gè)個(gè)去對(duì)返回體。2. 斷言語法速成pm.test、pm.expect 與四類基礎(chǔ)模板2.1 pm.test 的基本結(jié)構(gòu)與執(zhí)行規(guī)則Postman 斷言的核心入口是 pm 對(duì)象最常用的方法是pm.test?;A(chǔ)結(jié)構(gòu)其實(shí)非常簡單pm.test(你要描述的結(jié)果, function () { // 這里放斷言邏輯 });第一個(gè)參數(shù)是這條測(cè)試的名字會(huì)顯示在 Postman 的測(cè)試結(jié)果面板里第二個(gè)參數(shù)是回調(diào)函數(shù)函數(shù)內(nèi)部如果拋出了異?;蛘邤嘌圆煌ㄟ^這條測(cè)試就是失敗反之就是通過。這里有個(gè)新手經(jīng)常忽略的規(guī)則一個(gè)pm.test回調(diào)里可以寫多個(gè)pm.expect但前面某個(gè)斷言一旦失敗后面的代碼就不會(huì)繼續(xù)執(zhí)行。所以我的習(xí)慣是把不同維度的校驗(yàn)拆成多個(gè)pm.test而不是全部堆在一個(gè)回調(diào)里。這樣失敗的時(shí)候測(cè)試結(jié)果面板可以精確告訴你到底是哪一條沒通過而不是給你一個(gè)籠統(tǒng)的報(bào)錯(cuò)省去大量排查時(shí)間。2.2 高頻率使用的基礎(chǔ)斷言片段下面這組模板幾乎是我所有項(xiàng)目都會(huì)用到的基底直接復(fù)制到 Tests 標(biāo)簽頁按需刪減即可校驗(yàn)類型常用寫法說明狀態(tài)碼精確匹配pm.response.to.have.status(200);斷言精確狀態(tài)碼狀態(tài)碼大類判斷pm.response.to.be.success;2xx 全部通過客戶端錯(cuò)誤判斷pm.response.to.be.clientError;400-499 通過響應(yīng)時(shí)間pm.expect(pm.response.responseTime).to.be.below(500);響應(yīng)時(shí)間閾值響應(yīng)頭pm.expect(pm.response.headers.get(Content-Type)).to.include(application/json);響應(yīng)頭包含判斷字段存在pm.expect(jsonData).to.have.property(data);必填字段檢查字段類型pm.expect(jsonData.data.id).to.be.a(number);類型檢查對(duì)應(yīng)的完整代碼塊如下// 1. 狀態(tài)碼精確匹配 pm.test(狀態(tài)碼為 200, function () { pm.response.to.have.status(200); }); // 2. 狀態(tài)碼按大類判斷 pm.test(接口返回 2xx 成功, function () { pm.response.to.be.success; }); pm.test(接口返回 4xx 客戶端錯(cuò)誤, function () { pm.response.to.be.clientError; }); // 3. 響應(yīng)時(shí)間控制 pm.test(響應(yīng)時(shí)間小于 500ms, function () { pm.expect(pm.response.responseTime).to.be.below(500); }); // 4. 響應(yīng)頭斷言 pm.test(Content-Type 包含 application/json, function () { pm.expect(pm.response.headers.get(Content-Type)).to.include(application/json); }); // 5. 必填字段存在 pm.test(響應(yīng)包含 data 字段, function () { const jsonData pm.response.json(); pm.expect(jsonData).to.be.an(object); pm.expect(jsonData).to.have.property(data); });這里特別提醒一句判斷響應(yīng)頭時(shí)不要用eql(application/json)很多接口實(shí)際返回的是application/json; charsetutf-8精確匹配會(huì)一直失敗。用include做包含判斷才是穩(wěn)妥做法。2.3 給登錄接口配上第一條斷言的完整示例假設(shè)登錄接口登錄成功時(shí)返回token和用戶昵稱失敗時(shí)返回code和message。最基礎(chǔ)的斷言模板可以這樣寫pm.test(登錄接口返回用戶信息, function () { const jsonData pm.response.json(); pm.expect(jsonData.status).to.eql(success); pm.expect(jsonData).to.have.property(token); pm.expect(jsonData.data.nickname).to.be.a(string); }); pm.test(token 非空, function () { const token pm.response.json().token; pm.expect(token.length).to.be.greaterThan(0); });跑一次你就會(huì)直觀感受到斷言的威力以后任何人改動(dòng)接口返回哪怕只是把nickname字段名改成了nick_name測(cè)試面板都會(huì)立刻紅燈報(bào)警不需要測(cè)試人員再手動(dòng)對(duì)一遍響應(yīng)體。這類校驗(yàn)一旦形成習(xí)慣接口質(zhì)量會(huì)顯著提升因?yàn)槊恳淮胃膭?dòng)都有一雙“機(jī)器眼睛”在盯著。3. 業(yè)務(wù)校驗(yàn)?zāi)0鍙摹坝凶侄巍钡健白侄沃岛侠怼?.1 JSON 鍵、值、數(shù)組長度的組合校驗(yàn)只檢查字段存在其實(shí)只能覆蓋結(jié)構(gòu)層業(yè)務(wù)層的校驗(yàn)要再往下沉一層字段值對(duì)不對(duì)、組合起來成不成立。以列表接口為例我們往往要看data.list是不是數(shù)組、是否有數(shù)據(jù)、第一條記錄的 id 是否為數(shù)字const list pm.response.json().data.list; pm.test(list 是數(shù)組, function () { pm.expect(list).to.be.an(array); }); pm.test(list 非空, function () { pm.expect(list.length).to.be.greaterThan(0); }); pm.test(第一條數(shù)據(jù)包含 id 且為數(shù)字, function () { pm.expect(list[0]).to.have.property(id); pm.expect(list[0].id).to.be.a(number); });這里有一個(gè)非常實(shí)用的特性多個(gè)pm.test之間是相互獨(dú)立的。如果data.list本身不存在那么“l(fā)ist 是數(shù)組”會(huì)失敗但后面兩條依然會(huì)繼續(xù)執(zhí)行并給出各自的失敗信息。這比把所有斷言塞進(jìn)一個(gè)函數(shù)里要直觀得多排查問題時(shí)可以直接看到是哪一層出的錯(cuò)。3.2 嵌套 JSON 與動(dòng)態(tài)字段的校驗(yàn)方式嵌套 JSON 是業(yè)務(wù)校驗(yàn)最容易寫崩的地方。三層嵌套以上的接口我一般建議一層層剝開來斷言。假設(shè)訂單接口返回如下結(jié)構(gòu){ data: { order: { customer: { phone: 13800000000 }, items: [ { sku: SKU-001 }, { sku: SKU-002 } ] } } }可以這樣寫const order pm.response.json().data.order; pm.test(顧客手機(jī)號(hào)存在且格式正確, function () { pm.expect(order.customer).to.have.property(phone); pm.expect(order.customer.phone).to.match(/^1\d{10}$/); }); pm.test(訂單包含至少一個(gè)商品, function () { pm.expect(order.items).to.be.an(array); pm.expect(order.items.length).to.be.greaterThan(0); }); pm.test(商品 sku 格式正確, function () { order.items.forEach(function (item) { pm.expect(item.sku).to.match(/^SKU-\d{3}$/); }); });動(dòng)態(tài)字段是另一類高頻問題。接口里常見的timestamp、uuid、orderNo這類值每次請(qǐng)求都不一樣。對(duì)這種字段的斷言不要死磕具體值而是要校驗(yàn)格式和存在性。比如訂單號(hào)是ORD20250101001這類規(guī)則就斷言它匹配/^ORD\d{13}$/而不是把某一個(gè)固定訂單號(hào)寫死。否則每次數(shù)據(jù)一變你的斷言也跟著飄到最后沒人敢相信紅燈是不是真問題。3.3 響應(yīng)時(shí)間與響應(yīng)頭那些容易被忽略的檢查點(diǎn)響應(yīng)時(shí)間斷言本身不復(fù)雜但閾值一定要按接口性質(zhì)來定。用戶查詢、登錄這類高頻接口我一般卡在 500ms 以內(nèi)報(bào)表類、導(dǎo)出類接口2 到 3 秒都算正常。如果全團(tuán)隊(duì)共用一個(gè)統(tǒng)一閾值結(jié)果就是天天誤報(bào)最后大家直接把這條例注釋掉性能回歸形同虛設(shè)。響應(yīng)頭方面最容易踩的坑有三個(gè)。第一是 Content-Type 被精確匹配坑到前面已經(jīng)說過。第二是接口實(shí)際返回了text/html比如網(wǎng)關(guān)把錯(cuò)誤請(qǐng)求改寫成了 HTML 錯(cuò)誤頁此時(shí)你去調(diào)用pm.response.json()會(huì)直接拋異常這種問題在斷言里一眼就能暴露出來。第三是響應(yīng)頭缺少某些安全字段比如Cache-Control、X-Content-Type-Options這類檢查適合放到安全測(cè)試模板里用一個(gè)單獨(dú)的 pm.test 去斷言不影響業(yè)務(wù)校驗(yàn)?zāi)_本的可讀性。4. 閉環(huán)接口校驗(yàn)通過斷言提取變量并銜接下一個(gè)請(qǐng)求4.1 從響應(yīng)中抽取 token 的正確姿勢(shì)接口測(cè)試經(jīng)常要串聯(lián)登錄狀態(tài)先登錄拿 token再把 token 傳給其他接口。這一步我最常踩的坑是把pm.environment.set()寫在pm.test回調(diào)的后面。原因前面講過pm.test回調(diào)里如果前面的斷言失敗后面的賦值語句根本不會(huì)執(zhí)行。等下一個(gè)請(qǐng)求來取 token 時(shí)拿到的是上一次殘留值或者 undefined整個(gè)集合跑下來到處都是莫名其妙的 401。我現(xiàn)在習(xí)慣把提取動(dòng)作放到斷言之前const token pm.response.json().token; pm.environment.set(accessToken, token); pm.test(登錄接口返回 token, function () { pm.expect(token).to.be.a(string); pm.expect(token.length).to.be.greaterThan(20); });先提取再斷言只要響應(yīng)體里存在 token變量就一定會(huì)寫入環(huán)境不會(huì)因?yàn)樾r?yàn)失敗而影響后續(xù)請(qǐng)求鏈。這個(gè)順序上的小調(diào)整幫我省下了大量排查“為什么下游接口拿不到 token”的時(shí)間。4.2 用環(huán)境變量串聯(lián)登錄態(tài)與下游接口校驗(yàn)提取完 token 之后下游接口的請(qǐng)求頭或請(qǐng)求體里直接用{{accessToken}}引用即可比如Authorization: Bearer {{accessToken}}而在下游接口的 Tests 腳本里可以繼續(xù)做閉環(huán)校驗(yàn)驗(yàn)證本次請(qǐng)求的確帶上了正確的身份數(shù)據(jù)pm.test(下游接口返回當(dāng)前用戶訂單, function () { const jsonData pm.response.json(); pm.expect(jsonData.data.owner).to.eql(pm.environment.get(expectedUser)); });這樣整個(gè)集合跑起來登錄接口負(fù)責(zé)把變量喂給后續(xù)請(qǐng)求后續(xù)請(qǐng)求再把業(yè)務(wù)結(jié)果驗(yàn)回來鏈路就閉環(huán)了。相比人工拿著返回的 token 去復(fù)制粘貼這種自動(dòng)化關(guān)聯(lián)的方式更可靠而且變量值變更時(shí)不需要改任何腳本。4.3 變量優(yōu)先級(jí)與順序執(zhí)行時(shí)的坑Postman 取變量不是簡單的“后設(shè)的就覆蓋先設(shè)的”而是有明確的優(yōu)先級(jí)從高到低依次是局部變量、數(shù)據(jù)文件變量、環(huán)境變量、集合變量、全局變量。這意味著一個(gè)很現(xiàn)實(shí)的坑如果你在集合變量里配了username環(huán)境變量里也配了username而腳本里用的是pm.variables.get(username)最終拿到的優(yōu)先級(jí)更高的那個(gè)有可能是環(huán)境變量而不是集合變量。多環(huán)境混用的時(shí)候這種“變量看起來是對(duì)的但就是匹配不上”的問題特別難排查。我自己的做法是同一個(gè)業(yè)務(wù)含義的變量只放在一個(gè)層級(jí)不要同時(shí)鋪在很多層避免取到意料之外的舊值。順序執(zhí)行的坑也一樣常見。集合里多個(gè)請(qǐng)求串聯(lián)時(shí)如果變量是上一次運(yùn)行殘留的很可能出現(xiàn)假通過。我的習(xí)慣是在集合的 pre-request 腳本里做一次清理pm.environment.unset(accessToken);每次跑集合都是干凈的初始狀態(tài)絕不讓上次的 token 殘留到下次測(cè)試?yán)铩?. 數(shù)據(jù)驅(qū)動(dòng)與集合運(yùn)行讓斷言批量執(zhí)行還不誤報(bào)5.1 用 CSV / JSON 數(shù)據(jù)文件把測(cè)試數(shù)據(jù)分離出來單條請(qǐng)求寫死參數(shù)斷言也寫死期望值做十組用例就要復(fù)制十個(gè)請(qǐng)求維護(hù)成本非常高。數(shù)據(jù)驅(qū)動(dòng)的方式可以很好地解決這個(gè)問題把輸入?yún)?shù)和期望結(jié)果放到 CSV 或 JSON 文件里Postman 每次迭代讀取一行斷言腳本里直接用data變量引用當(dāng)前行數(shù)據(jù)。JSON 數(shù)據(jù)文件比 CSV 更靈活因?yàn)樗鼙磉_(dá)嵌套結(jié)構(gòu)。一個(gè)典型的登錄接口數(shù)據(jù)文件長這樣[ { username: alice, password: correct_pwd, expectSuccess: true }, { username: alice, password: wrong_pwd, expectSuccess: false } ]對(duì)應(yīng)的 Tests 腳本pm.test(登錄結(jié)果符合數(shù)據(jù)文件預(yù)期, function () { const jsonData pm.response.json(); if (data.expectSuccess) { pm.expect(jsonData.status).to.eql(success); } else { pm.expect(jsonData.status).to.eql(fail); } });數(shù)據(jù)與斷言分離之后新增測(cè)試用例只需要在數(shù)據(jù)文件里加一行不用再去復(fù)制請(qǐng)求和改腳本。這對(duì)測(cè)試團(tuán)隊(duì)維護(hù)用例庫來說是質(zhì)的提升。5.2 Collection Runner 與 Newman 的斷言結(jié)果解讀在 Postman 里選擇集合點(diǎn)擊 Run進(jìn)入 Collection Runner選好環(huán)境、數(shù)據(jù)文件、迭代次數(shù)就能批量執(zhí)行。跑完之后面板上會(huì)顯示 Pass/Fail 數(shù)量這是所有斷言的綜合結(jié)果。這里特別強(qiáng)調(diào)一句千萬不要只關(guān)心請(qǐng)求成功了多少個(gè)一定要去看斷言通過率這才是接口真正質(zhì)量情況的度量。Collection Runner 里還有一個(gè)容易被忽略的選項(xiàng)叫 Delay也就是每次請(qǐng)求之間的間隔毫秒數(shù)。接口有頻控、數(shù)據(jù)落庫有延遲的時(shí)候不加 Delay 會(huì)導(dǎo)致整批測(cè)試瞬間全掛。我一般至少設(shè)置 300ms 的延遲寧可跑得慢一點(diǎn)也不讓時(shí)序問題干擾斷言結(jié)果。Newman 是 Postman 的命令行版適合接 CI/CD。在本地跑通集合之后一條命令就能在流水線里復(fù)現(xiàn)同樣的斷言結(jié)果newman run collection.json -e environment.json -d data.json相關(guān)參數(shù)可以做成腳本放進(jìn)團(tuán)隊(duì)公共倉庫比每個(gè)人都在本地打開界面點(diǎn)按鈕要可控得多。5.3 跨環(huán)境運(yùn)行時(shí)如何避免斷言失真團(tuán)隊(duì)一般會(huì)有 dev、sit、uat 多套環(huán)境同一套集合在不同環(huán)境下跑斷言最容易翻車。原因通常有兩個(gè)一是只把域名放進(jìn)了環(huán)境變量但期望值寫死二是某個(gè)環(huán)境的數(shù)據(jù)庫被重置過導(dǎo)致很多非空判斷失敗。我的建議是域名、賬號(hào)、密碼、業(yè)務(wù)開關(guān)都抽成環(huán)境變量斷言里只寫通用的格式校驗(yàn)和狀態(tài)校驗(yàn)。確實(shí)需要校驗(yàn)具體業(yè)務(wù)值的時(shí)候把值也放到環(huán)境變量或數(shù)據(jù)文件字段里不要寫死在腳本里。比如“列表第一條 id 大于 0”是通用斷言跨環(huán)境都能跑“用戶是 vip 等級(jí) 3”這種就放到具體環(huán)境配置里按環(huán)境差異化處理。這樣模板庫才能在多套環(huán)境之間平滑復(fù)用。6. 斷言踩坑實(shí)錄誤報(bào)、漏報(bào)和腳本異常怎么排查6.1 空響應(yīng)與 JSON 解析異常pm.response.json()這個(gè)方法本身沒毛病但響應(yīng)體不是合法 JSON 的時(shí)候它會(huì)直接拋異常。最常見的三種情況請(qǐng)求超時(shí)返回空字符串、網(wǎng)關(guān)返回 HTML 錯(cuò)誤頁、代理把響應(yīng)改成了純文本。我現(xiàn)在處理這類場(chǎng)景有一套固定模板const rawText pm.response.text(); pm.test(響應(yīng)體非空, function () { pm.expect(rawText.length).to.be.greaterThan(0); }); pm.test(響應(yīng)體是合法 JSON 對(duì)象, function () { pm.expect(JSON.parse(rawText)).to.be.an(object); });先把text()拿出來判斷是否為空再JSON.parse確認(rèn)是合法 JSON最后才做字段和業(yè)務(wù)斷言。這樣一旦響應(yīng)體不是 JSON你能立刻看出問題是“空響應(yīng)”還是“響應(yīng)被中間層改寫了”而不是只看一個(gè)莫名其妙的腳本錯(cuò)誤。6.2 類型不一致、逗號(hào)和編碼帶來的值比較陷阱值比較是最容易誤報(bào)的地方。我說一個(gè)很典型的接口把數(shù)字字段total返回成字符串200而斷言里寫的是數(shù)字 200。eql(200)和eql(200)是兩個(gè)完全不同的判斷前者會(huì)通過后者會(huì)失敗。遇到這類問題先確認(rèn)接口文檔約定的是字符串還是數(shù)字別在斷言里想當(dāng)然。金額字段是另一個(gè)重災(zāi)區(qū)。某些系統(tǒng)會(huì)把金額格式化成1,200.50這樣的帶逗號(hào)字符串直接和數(shù)字比較怎么都不可能通過。這種一般先做格式校驗(yàn)再斷言const total pm.response.json().data.total; // 1,200.50 pm.test(金額字段格式正確, function () { pm.expect(total).to.match(/^\d{1,3}(,\d{3})*(\.\d{2})?$/); });正則維護(hù)起來雖然麻煩一點(diǎn)但比“看起來差不多”的字符串比較穩(wěn)定得多。還有編碼問題比如響應(yīng)里帶 BOM 頭或者特殊轉(zhuǎn)義符也會(huì)讓 JSON 解析和字符串比較翻車遇到這種狀況先看原始響應(yīng)文本再下手寫斷言。6.3 執(zhí)行順序帶來的“看起來失敗又看起來成功”接口之間有依賴關(guān)系時(shí)執(zhí)行順序錯(cuò)了斷言結(jié)果會(huì)非常難解釋。比如某個(gè)請(qǐng)求依賴前置請(qǐng)求寫入的orderId你在 Postman 里手動(dòng)點(diǎn)“發(fā)送”沒問題因?yàn)樯洗闻芰粝碌膐rderId還在環(huán)境里但放進(jìn) Collection Runner 從頭跑如果前置請(qǐng)求失敗了后續(xù)請(qǐng)求拿著一個(gè)過期 orderId接口返回“訂單不存在”這條斷言又會(huì)失敗。排查這類問題我有一個(gè)土辦法在斷言里把關(guān)鍵入?yún)⒁泊蛴〕扇罩綾onsole.log(orderId used in this request: , pm.environment.get(orderId));跑完看 Runner 的 Console 日志確認(rèn)每個(gè)請(qǐng)求真正用的是哪個(gè)變量就能判斷到底是前置請(qǐng)求掛了還是變量傳遞邏輯寫錯(cuò)了。這個(gè)排查思路比對(duì)著腳本反復(fù)猜要快得多我個(gè)人覺得是接口聯(lián)調(diào)階段最有價(jià)值的小技巧之一。7. 沉淀通用接口校驗(yàn)?zāi)0鍘鞆膫€(gè)人腳本到團(tuán)隊(duì)資產(chǎn)7.1 模板庫分層結(jié)構(gòu)與命名規(guī)范當(dāng)接口從幾個(gè)變成幾十個(gè)再變成幾百個(gè)之后靠記憶去維護(hù)斷言就完全不現(xiàn)實(shí)了。這時(shí)候需要把常用的校驗(yàn)邏輯抽成模板做成團(tuán)隊(duì)可復(fù)用的資產(chǎn)。我的做法是按“模塊-接口-校驗(yàn)點(diǎn)”三級(jí)組織。先建一個(gè) Postman Collection 作為接口校驗(yàn)?zāi)0鍘炖锩姘礃I(yè)務(wù)模塊建目錄每個(gè)請(qǐng)求的 Tests 腳本統(tǒng)一按校驗(yàn)點(diǎn)拆分。校驗(yàn)點(diǎn)命名用模塊_接口_校驗(yàn)內(nèi)容格式例如login_token_format登錄接口 token 格式校驗(yàn)order_list_nonEmpty訂單列表非空校驗(yàn)user_update_verifyFields用戶更新接口字段級(jí)校驗(yàn)這樣命名的好處很直接Collection Runner 跑完哪條斷言掛了一眼就能看到是哪個(gè)模塊、哪個(gè)接口、哪個(gè)校驗(yàn)點(diǎn)出了問題不需要展開腳本去讀代碼。7.2 可復(fù)用腳本塊與團(tuán)隊(duì)交接實(shí)踐經(jīng)常重復(fù)用到的腳本我習(xí)慣整理成標(biāo)準(zhǔn)片段單獨(dú)維護(hù)一份文檔隨集合一起更新。常見片段包括登錄態(tài)提取、通用字段存在性檢查、翻頁接口長度檢查、錯(cuò)誤碼斷言等等。團(tuán)隊(duì)交接的時(shí)候我會(huì)把集合導(dǎo)出成 JSON 文件提交到代碼倉庫同時(shí)附一份腳本片段說明文檔。新成員拿到之后不需要從零寫斷言直接按照片段往對(duì)應(yīng)接口里填充參數(shù)即可。模板庫的價(jià)值在于抬高團(tuán)隊(duì)水準(zhǔn)的下限不會(huì)因?yàn)檎l剛?cè)腴T就寫不出像樣的校驗(yàn)大家在這個(gè)框架里討論問題也更高效。7.3 模板庫維護(hù)過程中我比較在意的幾個(gè)細(xì)節(jié)維護(hù)模板庫半年之后有幾條經(jīng)驗(yàn)非常想分享。第一不要用一個(gè)大pm.test包裹幾十個(gè)斷言。一旦失敗你只能看到一個(gè)籠統(tǒng)的紅燈后面全靠猜。拆細(xì)一點(diǎn)測(cè)試結(jié)果列表本身就是一份問題清單哪條掛了一目了然。第二變量提取和斷言分離。這在前面提過放到模板庫層面更加重要。模板是給一整個(gè)團(tuán)隊(duì)復(fù)用的如果有人把賦值寫在斷言之后前置斷言一掛后續(xù)接口全部拿不到變量整個(gè)集合的串聯(lián)關(guān)系就斷了。第三統(tǒng)一處理空響應(yīng)和 JSON 解析。每個(gè)模板第一段固定是“響應(yīng)體非空 JSON 格式校驗(yàn)”這兩條過了后面的字段校驗(yàn)才有意義。模板庫要穩(wěn)定最底層這幾條基礎(chǔ)校驗(yàn)絕對(duì)不能省。