"Testing"
- 判準的推導來源:測試憑什麼不是實作的回音
測試與實作由同一次生成或同一輪對話產出時,用來判斷這組測試剩下多少驗證力,以及要動哪個變數才能拿回來
- Mock 邊界判斷決策表
什麼時候 mock 夠用、什麼時候需要真實服務 — 從 API 層 / 協議層 / 環境層的斷裂點判斷 mock 的適用範圍
- Protocol Integration Test
驗證程式碼和真實外部服務之間的協議互動是否正確的 test 層級
- Protocol integration test 定義
Protocol integration test 和 unit test / E2E test 的邊界 — 驗證程式碼和真實服務的協議契約,不驗證 UI 也不用 mock
- T.C1 WebSocket text/binary frame 被 FakeWebSocketChannel 遮蔽
Flutter app 用 Uint8List 發送 WS 資料走 binary frame,ttyd 期望 text frame 靜默忽略 — FakeWebSocketChannel 的 sink.add 接受 dynamic 不區分 frame type,192 個 test 全過但實機無回應
- Widget test 的狀態覆蓋策略
從畫面狀態矩陣推導 widget test case — 每個狀態的顯示、操作、退出路徑都是獨立的斷言目標
- 三層 log 設計
連線生命週期 log、protocol 訊息 log、使用者行為 log — 三層各自的職責、詳細程度和啟停控制
- 三層定義與職責表
Unit Test / Protocol Integration Test / Screen State Test 各層職責、驗證目標與盲區的完整論述
- 5.1 時間注入與狀態轉移測試
讓時間相關邏輯可重現
- 7.1 把 handler 邏輯拆成可測單元
分離 HTTP 協定處理與核心邏輯
- 驗收條件的等價類:關卡實際固定住了什麼
一組驗收條件通過就準備交付、卻說不出它放過了哪些行為時,用來攤出條件的維度、找沒被碰過的交叉並決定補哪一條
- Mock Masking(Mock 遮蔽)
mock 模擬 API 層但不模擬協議層,造成的結構性驗證盲區
- Mock 遮蔽機制分析
Mock 在 API 層、協議層、環境層之間製造的結構性盲區 — 斷裂點在哪、為什麼 mock 無法也不應該模擬協議行為
- T.C2 Auth handshake 邏輯缺失被 FakeWebSocketChannel 遮蔽
ttyd 連線後需要發送 auth token JSON frame 完成認證,整個邏輯未實作 — FakeWebSocketChannel 的 ready 立即完成不需認證,test 永遠看到連線成功
- Test data 代表性
手寫 vs 錄製 vs 生成三種測試資料來源 — 測試資料的代表性是一個隱性假設,決定了 test 能發現什麼問題
- WebSocket 協議測試實作
對真實 ttyd 驗證 frame type 和 auth handshake — 從 T.C1 和 T.C2 的教訓推導出的 protocol integration test 設計
- 功能規格中的 log 點定義方法
把 log 點設計從 debug 階段前移到功能規格階段 — 每個功能的規格文件新增可觀測性欄位,列出啟動 / 步驟 / 錯誤 / 完成四類 log 點
- 導航路徑 test
Back 按鈕、route 可達性、go vs push 語意 — 驗證使用者能從任何畫面回到預期的位置
- 5.2 testing 基礎
用 testing package 驗證函式行為
- 5.2 WebSocket integration test
驗證 client/server 實際互動
- 品質閘門的更替:覆蓋率、突變分數與各自的射程
測試數量大幅增加而信心沒有跟著上升、或要為生成的測試挑一個不會被灌水的門檻時,用來決定量什麼,以及那個量的盲區在哪裡
- 「名義 integration test」的識別與修正
test 名稱含 integration 但核心依賴全用 fake — 如何辨認、為什麼有害、怎麼修正命名和測試策略
- Assertion 品質三問
斷言的是行為嗎?能區分正確和錯誤嗎?會 flaky 嗎?— 三個問題判斷 assertion 是否有效
- HTTP contract test 設計
HTTP REST API 的 protocol integration test — request/response 格式、status code 語意、error body 結構的驗證
- Nominal Integration Test(名義整合測試)
名稱含 integration 但核心依賴全用 fake 的 test,驗證內部狀態機而非真實服務互動
- Playwright 瀏覽器驗證流程
用 Playwright 驗證 web 版本的 UI 行為 — test 結構、selector 策略、和 widget test 的互補關係
- T.C3 ANSI parser 測試資料不覆蓋真實 shell output
ANSI parser 只處理基本 SGR 色彩碼、unit test 用手寫乾淨字串驗證 — 真實 zsh prompt 送出 OSC 標題設定、CSI private mode 游標隱藏、括號貼上模式等數十種控制序列,全部殘留為亂碼
- 自架 log endpoint vs 商業方案的取捨判斷
自用工具用自架 log receiver(20 行 Go + grep)、商業 app 用 Sentry/Crashlytics — 判斷依據是使用者規模和 debug 需求
- 5.3 race condition 檢查
用 go test -race 找資料競爭
- 5.3 table-driven test
用表格整理多組輸入、預期輸出與錯誤情境
- 5.3 unittest 基礎
撰寫第一個單元測試
- 判準寫不下來的時候:性質、變形關係與留給人的部分
逐例預期值算不出來或跟不上產出速度時,用來決定判準退到哪一層、以及哪些驗證工作交不出去
- Hyprland VM 環境設定與測試矩陣
要在 VM 裡測試 Hyprland 配置、或判斷某個設定該在 VM 還是實機驗證時回來讀
- 「事後補 log」vs「設計產物 log」的品質差異
事後補的 log 是救火工具、設計產物的 log 是可觀測性基礎設施 — 從一次實機修復補 log 的教訓拆解兩者在格式、覆蓋率、維護成本上的差異
- CI 中的服務 fixture 管理
在 CI 中啟動和停止真實服務的 test harness 設計 — Process.start / Docker / testcontainers 三種方案的適用場景
- Flaky test 根因分類
計時依賴 / 環境差異 / 資源競爭 / 非確定性輸出 — 四類 flaky test 根因的辨識和處理策略
- Screen State Test
驗證使用者可見的畫面狀態覆蓋度和狀態間轉換完整性的 test 層級
- T.C4 Client-side log 缺失導致 debug 只能靠實機盲測
Flutter app 六個核心元件中只有兩個有 log(且全是 W2 hotfix 補的),連線失敗時開發者無法從任何 log 判斷失敗發生在哪一步 — 被迫用最昂貴的 debug 方式:插拔裝置反覆測試
- 判斷原則:什麼時候需要 protocol integration test
從服務架構特徵判斷是否需要 protocol integration test 的決策流程 — 協議複雜度、mock 寬鬆度、失敗靜默度三個維度
- 螢幕截圖比對
Visual regression testing — 用螢幕截圖比對偵測非預期的視覺變化、baseline 管理和 diff 閾值設定
- 5.4 HTTP handler 測試
用 httptest 驗證 request 與 response
- 5.4 table-driven test 的設計邊界
避免測試資料混雜太多概念
- 5.4 Mock 與測試隔離
隔離外部依賴
- Flaky test 團隊治理
flaky test 累積到讓團隊開始 skip 或 ignore 時,從個案修復升級到團隊層的治理策略:quarantine 政策、retry 預算、信任修復的可視化與行動閾值
- Semantic Fake Backend(語意級假後端)
持有狀態、只固化已證實後端行為的測試假件;與由測試餵資料的 stub 以狀態歸屬和行為出處劃界
- T.C5 凍結參照失效被 stub 遮蔽 — 測試全綠、功能全壞
前端把後端資料的 id 凍結在本地記錄裡,後端的合併操作會重建資料、舊 id 全部失效 — 單元測試的 stub 由測試自己餵資料,永遠不會經歷「id 死亡」,bug 存在期間所有測試綠燈
- 反模式:用 mock 數量彌補 mock 盲區
為什麼增加 mock test 數量無法跨越 mock 的結構性盲區 — 從 192 個 test 全過的案例拆解數量與覆蓋率的真正關係
- 成本判斷表
什麼時候值得寫 protocol integration test、什麼時候用 contract test 或實機測試替代 — 根據服務啟動成本和協議複雜度判斷
- 開發環境 vs 真機的 gate 行為差異表
模擬器、debug build、test 環境中的 gate 行為和真機 release build 不同 — 差異表讓開發者在上機前知道哪些 gate 還沒被真實驗證
- 5.5 時間注入與 deterministic test
用 time provider 避免測試依賴真實時間
- Flow Test(流程測試)
在假後端上驅動真實前端服務鏈、斷言散佈於業務旅程各階段的測試形態;與 unit / integration / E2E 的邊界劃分
- T.C6 流程測試首跑抓到修復自己引入的順序 bug
單元測試直接把狀態塞進被測物、繞過了真實的資料過濾鏈;流程測試讓資料走完整鏈路,第一次執行就抓到「刷新在列表同步之前」的編排順序錯誤 — 而這個錯誤正是前一個 bug 的修復引入的
- 真實後端驗證測試
服務無法本機啟動、只有共用測試環境(staging)時,把對真實後端的行為驗證寫成常駐測試:離線降級為跳過、憑證失效必須紅燈,讓假後端固化的行為假設有地方對真實後端驗證
- 測試註解與命名紀律
測試註解寫什麼、名稱與 reason 怎麼收斂、分析詞彙與開發過程該不該進程式碼 — 判斷測試文字去留的紀律
- 語意級假後端與流程測試
bug 的成因是對後端行為的假設錯誤、由測試餵資料的 stub 驗證不出來時:建一個持有狀態、模擬已證實後端行為的假後端(test double 分類的 fake),讓流程測試走完整的多服務互動鏈
- 組裝層的可達性
行為測試全綠、功能在實機上沒有入口的失效形態出現時使用。mock 換掉的正是組裝,組裝完成與否在行為測試裡沒有證言;把可達性當成組裝層的不變式,在測試、發版與設計文件各給一個強制點。
- 5.6 並發行為測試
測試 channel、goroutine 與狀態更新
- 7.6 CI、fuzz、load test 與 chaos testing
把單元測試與整合測試擴展成服務可靠性驗證流程
- 測試的價值發生在它變紅的那一刻——建立、變更與註解分工的防護視角
為一段邏輯決定第一條測試該測什麼、變更或重構前評估既有測試會擋什麼、review 裡爭論某個約束該寫註解還是測試、或拿到紅燈要判讀它是刻意規則還是漏網 bug 時使用。這些問題共用同一個視角:測試的價值在未來的紅燈、不在當下的綠燈。
- Real-Backend Verification Test(真實後端驗證測試)
對共用測試環境常駐斷言後端業務行為的測試;紅、綠、跳過各有語意,承接假後端固化行為的漂移警報
- T.C7 症狀相同、成因兩種 — 用測試切開前後端責任
刪除單據後資源佔用狀態未還原:可能是後端沒釋放,也可能是後端釋放了但前端沒刷新畫面 — 兩種成因症狀完全相同。假後端模擬兩種行為各寫一條測試、再用真實後端測試一跑定案
- 測試憑證管理
測試環境的帳號密碼放在哪裡、CI 怎麼拿到、怎麼防止對生產環境執行 — 存放策略的適用前提與失效偵測
- 無測試 legacy 專案的起步順序
接手零測試的專案、有限預算下第一批測試該從哪一層開始建——按風險集中處判斷起步路徑,而非照測試金字塔從底部往上疊
- 5.7 錯誤處理與測試在高併發服務中的角色
把錯誤路徑、測試保護與並發行為放進服務可靠性觀點
- Fire-and-Forget Orchestration(射後不理編排)
呼叫後不等待完成的編排形態;產生「方法返回不等於流程完成」的時序落差,是 flaky test 的常見根因之一
- T.C8 fire-and-forget 編排讓測試單跑綠、合跑紅
結帳成功後的收尾動作(列印、狀態清理、資料同步)沒有被 await — 測試斷言與收尾動作賽跑,單獨執行時碰巧贏、整批執行時排程不同就輸;競態本身也揭露了產品的時序特性
- Frozen vs Live Reference(凍結參照與活解析)
下游持有上游 id 的兩種策略——寫死 id vs 查詢時動態解引用;上游重建後凍結參照失效,stub 對此結構性盲目
- T.C9 外接螢幕漏通知 — 訊息序列斷言與訂閱盲區
客戶端把狀態同步到外接第二螢幕:訂閱驅動的路徑自動生效,顯式呼叫的路徑每開一條新流程就可能漏 — 漏通知 bug 的結構根因。驗證方式是錄音假件攔下送出的訊息序列,斷言「該送什麼、何時送、誰先誰後」
- T.C10 同一組驗收通過八個不同的程式 — 關卡放行的等價類有多大
同一份需求重寫多次、產出的程式互不相同卻全部通過同一組驗收時,用來看清一組關卡實際固定住了哪些行為,又放過了哪些維度
- Characterization Test
斷言「行為不變」而非「行為正確」的測試形態;與正確性測試的語意分界,決定紅燈能不能歸因
- Quarantine(隔離)
把已知 flaky 的測試從主要執行路徑移開、保留修復壓力的治理機制;與 skip 的語意差異在於 quarantine 有負責人和回收期限
- Test Seam
測試想把真實依賴換成替身、該從哪裡換時使用。seam 是不修改程式本體就能替換其中一段行為的位置——介面加注入點是物件導向最常見的形式。
- Test Environment Identification(測試環境判定)
會寫入的自動化測試在執行前確認目標環境的機制;它是憑證管理與真實後端驗證的共同上游依賴,判定不了時的預設行為決定這條防線的強度
- Wiring Test
行為測試全綠、還想知道組裝有沒有完成時使用。接線測試在組裝路徑零 override 的環境解析每個注入項、走真實路由表,只驗「port 插上了 adapter」。
- Skip vs Fail Semantics(跳過與失敗的語意)
測試遇到無法驗證的狀態時,跳過與失敗兩種訊號各自對應的成因類別與處置路徑
- Stub
測試作者手動寫死回應資料的 test double:驗證的是假設成立時邏輯是否正確,假設本身錯誤時無法檢出
- Consumer-Driven Contract Test
client 與 server 分屬不同團隊、後端無法本機或容器啟動時,用契約取代對真實服務的協議整合測試
- 用前端測試把排版問題自動化
排版問題傳統靠人眼檢查、容易遺漏邊界 case。當一個版型被 debug 兩次以上、就值得寫成 playwright 測試把規範固定下來。本文展開測試替代手動檢查的時機。
- Test Double Taxonomy(Fowler 分類)
test double 依據回應資料的來源與驗證方式分成五種角色,各自對應不同的可測範圍與盲區
- Test Oracle(測試判準來源)
一個測試憑什麼判定通過或失敗說不清楚、或被測對象沒有可逐例算出的正確答案時,用來定位判準的來源,以及每一種來源的射程
- Test Provenance(測試出處)
測試與被測實作由同一來源產出時(同一次生成、同一輪對話、同一個人同時寫),用來判斷這組測試還剩下多少驗證力
- Testing vs Checking(探究與檢查)
斷言可以被整批產生之後,用來區分哪些驗證工作交得出去給機器、哪些需要人的判斷才成立
- Mutation Testing(突變測試)
行覆蓋率很高卻仍漏掉 bug、或要挑一個比覆蓋率難灌水的測試品質指標時,用來判斷這套測試實際擋得住什麼
- Property-Based Testing(性質式測試)
逐例斷言列不完、或預期輸出難以逐例算出時,用來把驗證對象從個別例子換成對所有輸入都該成立的性質
- Metamorphic Testing(變形測試)
被測對象沒有已知正確輸出、連不變量都難以陳述時,用兩次執行之間的關係取代預期值
- Cyclomatic Complexity(圈複雜度)
看到函式長度或複雜度上限這類數字門檻時,用來判斷它量的是什麼、以及達成它的兩條路徑為什麼在指標上分不出來
- 驗證自己寫對了
不確定該測到什麼程度、或是在「這裡到底該不該 mock」上卡住時,用來看清這幾本書彼此不同意在哪的選讀
- 註解防不了改壞——防護需求要交給會發聲的機制
想給欄位或函式寫一行 doc、或審查一則宣稱約束的註解時使用。判斷這個資訊該由測試、型別、命名還是結構承接,以及約束本身能不能先被消除。
- StreamProvider 包 repository watch stream — broadcast、初始值、dispose 實作點
repository 要補 Stream 觀測出口、接給 Riverpod 消費時使用。訂閱模型選 broadcast 還是單訂閱、新訂閱者拿不拿得到當下狀態、controller 誰負責關——每個問題各有一個會靜默失效的預設答案。
- 一行查詢放哪、一個測試留不留:結構改動後的兩次「還需要存在嗎」
修完一個應收金額 bug 收尾時,兩個『還需不需要』的問題浮出來。一段『過濾出已結帳品項』的查詢該掛哪一層——inline 在 widget 是洩漏、開一個 service 是儀式,判準是『查詢誰擁有的資料就掛給誰』,答案是既有的 repository。一個為了鎖住修復而寫的測試該不該留——當修復是靠刪掉舊函式、移除參數達成時,那個 bug 被結構免疫了,測試變贅述;判準是『這測試守的失敗模式,結構是否已經替它擋掉』。
- 測試全綠、功能失聯:五個 runtime 問題與組裝層的接線缺口
113 張票收尾全綠的版本,實機測試找出五個問題:路由指向佔位頁、provider 佔位 throw、按鈕空 callback,加上兩個平台語意差異。單元測試的 mock override 是正當的測試 seam、同時是遮蔽接線斷裂的來源——記下三層共振的機制、反向追溯設計文件的結果、以及修補時規格層/測試層/發版層的分層落點。
- 「該收多少錢」抽成 pure function — IO 在邊界、領域計算在核心
多個畫面都要顯示「未結帳的份數與金額」時,把計算抽成無 IO 的 pure function:資料由 caller 從 repository 拿好傳入、函式只做合併 / 扣減 / 折扣運算。含合併鍵要跟同一性定義同維度的陷阱、兩層折扣各自 clamp 的邊界、以及用註解預留擴充點讓未來規則接入不動本體。
- 1101 行自建測試基礎設施、重構刪掉 82.5% — 過度工程的三種形態
自建測試基礎設施前先問框架的標準做法是什麼:mock 純資料物件、helper 帶並發鎖與記憶體洩漏防護、mock 放進 lib/ 進生產依賴圖,三種形態都在重新發明 Riverpod overrideWith 一行就有的東西。精緻的設計文件不是價值證明——它可以精心規劃一個不需要存在的系統。
- 16 個失敗只有 2 個是缺口 — 大規模測試失敗先分診、再按 ROI 修
測試失敗數超過十個時逐個修是錯的順序:先全數分類根因、再按「單位工時救回的測試數」排修復順序。兩批實戰分類顯示半數失敗是斷言過時而非 bug、九個失敗共用一個 helper 修法;每類的症狀特徵字串可以建成索引讓下批失敗直接對號。
- mock 要配置 55 個方法、實際只用 5 個 — 測試痛是介面設計痛的探針
service 測試的 mock 負擔正比於它依賴的介面寬度:依賴四個大介面共 55 個方法、實際呼叫 5 個,91% 的 mock 配置是純浪費、還會炸 MissingStubError。修法是介面隔離——抽出只含實際使用方法的 Port,讓 mock 縮到跟真實依賴一樣窄;為未來預留的方法用 TODO 標記啟用時機。
- 功能「完成」、測試全過、資料從未落地 — 持久化迴圈是驗收的盲區
domain 功能的測試可以全綠、而它的資料從未被序列化、資料庫沒有對應的表——單元測試都在記憶體內驗證行為、沒有一條測試走「存進去、重建、讀出來」的迴圈。驗收定義要含 roundtrip;entity 欄位與 schema 欄位的差集是靜默資料失真的清單。
- 兩個 domain 各自實作同一個 API service — 100% 覆蓋率的假象
同名 service 在多個 domain 各自實作時,覆蓋率數字會失去意義:每份實作各測各的、mock 各有介面,統一的行為從未被測過。重複實作是上游訊號——規劃文件沒抽出跨 domain 的共同技術需求;單檔品質審查看不到跨檔重複。
- 取個原始值有四種寫法 — VO 的 toString 洩漏與 accessor 不一致
value object 家族的取值 accessor 各自為政(displayValue、toString、裸欄位)時,殺傷力會在測試層爆開:自定義 toString 讓裸字串斷言全數過期、四種取法讓修復者自己都寫錯。止血是 helper 函數庫集中取值知識、根治是家族統一 accessor 命名。
- 紅燈在量什麼 — 測試訊號的三層失真:斷言、量測、環境
「全套件降至 0」有意義的前置條件是紅燈只反映程式缺陷。三層各自會失真:絕對計時斷言量的是機器負載、compact reporter 高並行下行覆寫產生假陰性、fresh checkout 缺 gitignored 生成產物讓整包結果不可信。含 flaky 判定的取樣門檻與對照實驗定歸因的做法。
- 產品碼自己是 mock — ViewModel 假實作通過了 15 個測試
ViewModel 用 Future.delayed 加硬編碼資料交付、狀態轉換測試全綠,因為測試耦合的是狀態機、不是業務效果。假實作被當成完成的基礎,直到 63 個測試「寫不出來」才現形——測試的可寫性是實作真實性的探針。
- 測「不變」、不測「正確」 — characterization test 當遷移安全網
大規模型別遷移前,對著舊實作寫一批鎖住現有行為的測試——包括看起來像 bug 的邊界怪癖也照鎖,遷移後全綠證明「換底沒改行為」。正確性是另一批測試的職責、混在一起紅燈就無法歸因。附測試環境的原生依賴三個斷點(FFI、plugin、late init)與替身解法。
- 遷移計畫有寫入、有消費、缺讀出 — read-path 缺口與 fixture 假綠
資料模型遷移的通路要三段齊:寫入 backfill、讀取路徑、消費端 API。缺讀出那段時,新 API 拿到的永遠是空集合——而消費端測試的 fixture 自己建物件、不走真實讀取路徑,測試全綠掩蓋 runtime 靜默失效。依賴圖只列「誰先做」不列語意前提時,dashboard 的 ready 是假訊號。
- 新增欄位忘記同步 reset — 跨測試狀態洩漏的系統性根因
測試結果取決於執行順序、看似功能 bug 實為上一個 test case 狀態沒清乾淨。根因是新增 private 欄位時沒同步更新 reset,隱含契約沒被顯性化。
- 10 個 Ticket、57 個綠燈、0 條追溯:從需求文件到測試的銜接檢討
單元測試全綠、卻對應不出「這些測試覆蓋了哪些 UseCase 場景」。需求到測試缺反向追溯時的流程缺口盤點與對應修法(追溯矩陣、存根策略、拆分規則)。
- 192 個測試全過、實機全壞:Mock 遮蔽真實行為的三層測試策略
unit test 全綠、實機部署後功能整片壞掉。mock-only 策略的結構盲區(text vs binary frame、缺 auth handshake、ANSI 多樣性被 FakeWebSocketChannel 遮蔽),以及分層測試各抓什麼、各遮蔽什麼。
- Firestore Security Rules Test Lab
用 @firebase/rules-unit-testing 在 emulator 上把 Security Rules 寫成自動化測試:放行 / 越權拒絕 / 未登入拒絕 / 欄位竄改拒絕四類斷言、firebase emulators:exec 在 CI 跑、把規則測試接進 release gate
- SQLite Test Fixture Best Practice
SQLite 作為 test fixture、repository contract test、production dialect gap、seed data、fixture snapshot 與 CI evidence 的操作判準
- 測試命名作為文件:可執行的規格說明
測試是少數會自我驗證的文件——名稱跟實際行為不符、CI 會炸。把測試命名寫成 state-based / scenario-based / failure-mode 三種模式的 spec 條目、配合 group 結構作為命名空間、讀者跳到測試檔掃名字就能取代讀 doc。
- Playwright in the Development Loop — 開發循環的三個位置
frontend-with-playwright reference:Playwright 三個位置(假設 / 行為 / 互動驗證)的 evaluate 範例、寫成 layout test 的時機與模板、最低門檻 setup。