<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Test-Double on Tarragon</title><link>https://tarrragon.github.io/blog/tags/test-double/</link><description>Recent content in Test-Double on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Mon, 20 Jul 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/test-double/index.xml" rel="self" type="application/rss+xml"/><item><title>Semantic Fake Backend（語意級假後端）</title><link>https://tarrragon.github.io/blog/testing/knowledge-cards/semantic-fake-backend/</link><pubDate>Fri, 17 Jul 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/testing/knowledge-cards/semantic-fake-backend/</guid><description>&lt;p>語意級假後端是一個持有狀態、只固化已證實後端行為的測試假件：多個前端服務對它走完整的互動鏈，每個操作演變它內部的狀態。在 test double 分類中它對應 Fowler 定義的 fake（有狀態、可運作的簡化實作），「語意級」限定的是行為出處——每一條行為都經過實測證實。它是&lt;a href="https://tarrragon.github.io/blog/testing/knowledge-cards/flow-test/" data-link-title="Flow Test（流程測試）" data-link-desc="在假後端上驅動真實前端服務鏈、斷言散佈於業務旅程各階段的測試形態；與 unit / integration / E2E 的邊界劃分">流程測試&lt;/a>的地基，與&lt;a href="https://tarrragon.github.io/blog/testing/knowledge-cards/real-backend-verification-test/" data-link-title="Real-Backend Verification Test（真實後端驗證測試）" data-link-desc="對共用測試環境常駐斷言後端業務行為的測試；紅、綠、跳過各有語意，承接假後端固化行為的漂移警報">真實後端驗證測試&lt;/a>配對運作。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>與「由測試餵資料的 stub」的分界有兩條：狀態的歸屬（stub 的回應由測試作者逐條寫死，假後端自己持有狀態並隨操作演變）、行為的出處（stub 回放作者對後端的假設，假後端只收錄實測證實的後端行為）。它與 &lt;a href="https://tarrragon.github.io/blog/testing/knowledge-cards/mock-masking/" data-link-title="Mock Masking（Mock 遮蔽）" data-link-desc="mock 模擬 API 層但不模擬協議層，造成的結構性驗證盲區">mock 遮蔽&lt;/a>批評的「讓 mock 更逼真」處在相異層次：假後端的模擬止於應用層行為，協議層仍歸 &lt;a href="https://tarrragon.github.io/blog/testing/knowledge-cards/protocol-integration-test/" data-link-title="Protocol Integration Test" data-link-desc="驗證程式碼和真實外部服務之間的協議互動是否正確的 test 層級">protocol integration test&lt;/a>。&lt;/p>
&lt;h2 id="可觀察訊號與例子">可觀察訊號與例子&lt;/h2>
&lt;p>多個前端服務對同一份後端狀態接力，且出現過「對後端行為假設錯誤」型的漏網 bug。典型例：後端合併資料時重建全部子項並更換 id，前端依賴&lt;a href="https://tarrragon.github.io/blog/testing/knowledge-cards/frozen-vs-live-reference/" data-link-title="Frozen vs Live Reference（凍結參照與活解析）" data-link-desc="下游持有上游 id 的兩種策略——寫死 id vs 查詢時動態解引用；上游重建後凍結參照失效，stub 對此結構性盲目">凍結參照&lt;/a>的錯誤在 stub 上永遠測不紅（&lt;a href="https://tarrragon.github.io/blog/testing/cases/stale-reference-stub-blindspot/" data-link-title="T.C5 凍結參照失效被 stub 遮蔽 — 測試全綠、功能全壞" data-link-desc="前端把後端資料的 id 凍結在本地記錄裡，後端的合併操作會重建資料、舊 id 全部失效 — 單元測試的 stub 由測試自己餵資料，永遠不會經歷「id 死亡」，bug 存在期間所有測試綠燈">T.C5&lt;/a>）。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>假後端要決定掛載接縫（in-process 假實作或本地假 server）、行為取證方式、與配對慣例——每一條行為假設對應一條真實後端驗證斷言。掛載判準、取證選單與成本量級的推導在&lt;a href="https://tarrragon.github.io/blog/testing/01-test-strategy-layers/semantic-fake-backend/" data-link-title="語意級假後端與流程測試" data-link-desc="bug 的成因是對後端行為的假設錯誤、由測試餵資料的 stub 驗證不出來時：建一個持有狀態、模擬已證實後端行為的假後端（test double 分類的 fake），讓流程測試走完整的多服務互動鏈">語意級假後端與流程測試&lt;/a>章。&lt;/p></description><content:encoded><![CDATA[<p>語意級假後端是一個持有狀態、只固化已證實後端行為的測試假件：多個前端服務對它走完整的互動鏈，每個操作演變它內部的狀態。在 test double 分類中它對應 Fowler 定義的 fake（有狀態、可運作的簡化實作），「語意級」限定的是行為出處——每一條行為都經過實測證實。它是<a href="/blog/testing/knowledge-cards/flow-test/" data-link-title="Flow Test（流程測試）" data-link-desc="在假後端上驅動真實前端服務鏈、斷言散佈於業務旅程各階段的測試形態；與 unit / integration / E2E 的邊界劃分">流程測試</a>的地基，與<a href="/blog/testing/knowledge-cards/real-backend-verification-test/" data-link-title="Real-Backend Verification Test（真實後端驗證測試）" data-link-desc="對共用測試環境常駐斷言後端業務行為的測試；紅、綠、跳過各有語意，承接假後端固化行為的漂移警報">真實後端驗證測試</a>配對運作。</p>
<h2 id="概念位置">概念位置</h2>
<p>與「由測試餵資料的 stub」的分界有兩條：狀態的歸屬（stub 的回應由測試作者逐條寫死，假後端自己持有狀態並隨操作演變）、行為的出處（stub 回放作者對後端的假設，假後端只收錄實測證實的後端行為）。它與 <a href="/blog/testing/knowledge-cards/mock-masking/" data-link-title="Mock Masking（Mock 遮蔽）" data-link-desc="mock 模擬 API 層但不模擬協議層，造成的結構性驗證盲區">mock 遮蔽</a>批評的「讓 mock 更逼真」處在相異層次：假後端的模擬止於應用層行為，協議層仍歸 <a href="/blog/testing/knowledge-cards/protocol-integration-test/" data-link-title="Protocol Integration Test" data-link-desc="驗證程式碼和真實外部服務之間的協議互動是否正確的 test 層級">protocol integration test</a>。</p>
<h2 id="可觀察訊號與例子">可觀察訊號與例子</h2>
<p>多個前端服務對同一份後端狀態接力，且出現過「對後端行為假設錯誤」型的漏網 bug。典型例：後端合併資料時重建全部子項並更換 id，前端依賴<a href="/blog/testing/knowledge-cards/frozen-vs-live-reference/" data-link-title="Frozen vs Live Reference（凍結參照與活解析）" data-link-desc="下游持有上游 id 的兩種策略——寫死 id vs 查詢時動態解引用；上游重建後凍結參照失效，stub 對此結構性盲目">凍結參照</a>的錯誤在 stub 上永遠測不紅（<a href="/blog/testing/cases/stale-reference-stub-blindspot/" data-link-title="T.C5 凍結參照失效被 stub 遮蔽 — 測試全綠、功能全壞" data-link-desc="前端把後端資料的 id 凍結在本地記錄裡，後端的合併操作會重建資料、舊 id 全部失效 — 單元測試的 stub 由測試自己餵資料，永遠不會經歷「id 死亡」，bug 存在期間所有測試綠燈">T.C5</a>）。</p>
<h2 id="設計責任">設計責任</h2>
<p>假後端要決定掛載接縫（in-process 假實作或本地假 server）、行為取證方式、與配對慣例——每一條行為假設對應一條真實後端驗證斷言。掛載判準、取證選單與成本量級的推導在<a href="/blog/testing/01-test-strategy-layers/semantic-fake-backend/" data-link-title="語意級假後端與流程測試" data-link-desc="bug 的成因是對後端行為的假設錯誤、由測試餵資料的 stub 驗證不出來時：建一個持有狀態、模擬已證實後端行為的假後端（test double 分類的 fake），讓流程測試走完整的多服務互動鏈">語意級假後端與流程測試</a>章。</p>
]]></content:encoded></item><item><title>Stub</title><link>https://tarrragon.github.io/blog/testing/knowledge-cards/stub/</link><pubDate>Mon, 20 Jul 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/testing/knowledge-cards/stub/</guid><description>&lt;p>Stub 是測試作者手動寫死回應資料的 test double：呼叫者拿到的每一筆資料，都是測試在建置階段逐條餵進去的固定值。它跟&lt;a href="https://tarrragon.github.io/blog/testing/knowledge-cards/semantic-fake-backend/" data-link-title="Semantic Fake Backend（語意級假後端）" data-link-desc="持有狀態、只固化已證實後端行為的測試假件；與由測試餵資料的 stub 以狀態歸屬和行為出處劃界">語意級假後端&lt;/a>的分野在狀態的歸屬——stub 的回應由測試作者寫死，假後端自己持有狀態、隨操作演變。Stub 回放的是測試作者對後端行為的假設，假設錯了，測試照樣綠燈，這是它的結構性限制，不是實作疏忽。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Stub 驗證的標的是「假設成立時、前端邏輯是否正確」，不是「假設本身是否成立」。假設與斷言出自同一人之手，永遠自洽——這一點讓 stub 沒有能力檢出「對後端行為的理解本身就錯了」這一類 bug。&lt;a href="https://tarrragon.github.io/blog/testing/knowledge-cards/mock-masking/" data-link-title="Mock Masking（Mock 遮蔽）" data-link-desc="mock 模擬 API 層但不模擬協議層，造成的結構性驗證盲區">Mock 遮蔽&lt;/a>描述的是協議層的結構性盲區，stub 的盲區發生在更上游：連需要驗證的行為假設都由同一人設計。兩種盲區疊加時，測試綠燈能證明的範圍比表面上小得多。Stub 在 test double 家族裡的位置（跟 dummy / spy / mock / fake 的分工全景）見 &lt;a href="https://tarrragon.github.io/blog/testing/knowledge-cards/test-double-taxonomy/" data-link-title="Test Double Taxonomy（Fowler 分類）" data-link-desc="test double 依據回應資料的來源與驗證方式分成五種角色，各自對應不同的可測範圍與盲區">Test Double Taxonomy&lt;/a>。&lt;/p>
&lt;h2 id="可觀察訊號與例子">可觀察訊號與例子&lt;/h2>
&lt;p>辨識訊號是「stub 資料與斷言預期出自同一次測試撰寫」——沒有獨立於測試作者的行為出處。典型案例：前端把後端資料的 id 凍結在本地記錄裡，單元測試建立記錄時順手把對應資料餵進 stub，凍結 id 與 stub 資料恆常一致；真實世界裡讓 id 失效的是後端的合併操作，stub 沒有這個行為，於是「id 失效」這個狀態在測試裡永遠不會出現，功能全壞、測試全綠（&lt;a href="https://tarrragon.github.io/blog/testing/cases/stale-reference-stub-blindspot/" data-link-title="T.C5 凍結參照失效被 stub 遮蔽 — 測試全綠、功能全壞" data-link-desc="前端把後端資料的 id 凍結在本地記錄裡，後端的合併操作會重建資料、舊 id 全部失效 — 單元測試的 stub 由測試自己餵資料，永遠不會經歷「id 死亡」，bug 存在期間所有測試綠燈">T.C5&lt;/a>）。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>判斷「stub 夠不夠」的問句是：如果把 stub 換成真實服務，斷言結果會不會改變？只驗證程式碼內部邏輯（狀態機轉換、錯誤處理分支）時，stub 夠用；驗證對象是「對後端行為的假設」本身時，stub 結構上驗證不出來，需要換成持有狀態、行為有實測出處的&lt;a href="https://tarrragon.github.io/blog/testing/knowledge-cards/semantic-fake-backend/" data-link-title="Semantic Fake Backend（語意級假後端）" data-link-desc="持有狀態、只固化已證實後端行為的測試假件；與由測試餵資料的 stub 以狀態歸屬和行為出處劃界">語意級假後端&lt;/a>。凡是前端持有後端 id 的地方，都要追問後端的哪些操作會讓這個 id 死亡，並把「id 失效」的劇本納入測試範圍——見&lt;a href="https://tarrragon.github.io/blog/testing/knowledge-cards/frozen-vs-live-reference/" data-link-title="Frozen vs Live Reference（凍結參照與活解析）" data-link-desc="下游持有上游 id 的兩種策略——寫死 id vs 查詢時動態解引用；上游重建後凍結參照失效，stub 對此結構性盲目">凍結參照與活解析&lt;/a>。&lt;/p></description><content:encoded><![CDATA[<p>Stub 是測試作者手動寫死回應資料的 test double：呼叫者拿到的每一筆資料，都是測試在建置階段逐條餵進去的固定值。它跟<a href="/blog/testing/knowledge-cards/semantic-fake-backend/" data-link-title="Semantic Fake Backend（語意級假後端）" data-link-desc="持有狀態、只固化已證實後端行為的測試假件；與由測試餵資料的 stub 以狀態歸屬和行為出處劃界">語意級假後端</a>的分野在狀態的歸屬——stub 的回應由測試作者寫死，假後端自己持有狀態、隨操作演變。Stub 回放的是測試作者對後端行為的假設，假設錯了，測試照樣綠燈，這是它的結構性限制，不是實作疏忽。</p>
<h2 id="概念位置">概念位置</h2>
<p>Stub 驗證的標的是「假設成立時、前端邏輯是否正確」，不是「假設本身是否成立」。假設與斷言出自同一人之手，永遠自洽——這一點讓 stub 沒有能力檢出「對後端行為的理解本身就錯了」這一類 bug。<a href="/blog/testing/knowledge-cards/mock-masking/" data-link-title="Mock Masking（Mock 遮蔽）" data-link-desc="mock 模擬 API 層但不模擬協議層，造成的結構性驗證盲區">Mock 遮蔽</a>描述的是協議層的結構性盲區，stub 的盲區發生在更上游：連需要驗證的行為假設都由同一人設計。兩種盲區疊加時，測試綠燈能證明的範圍比表面上小得多。Stub 在 test double 家族裡的位置（跟 dummy / spy / mock / fake 的分工全景）見 <a href="/blog/testing/knowledge-cards/test-double-taxonomy/" data-link-title="Test Double Taxonomy（Fowler 分類）" data-link-desc="test double 依據回應資料的來源與驗證方式分成五種角色，各自對應不同的可測範圍與盲區">Test Double Taxonomy</a>。</p>
<h2 id="可觀察訊號與例子">可觀察訊號與例子</h2>
<p>辨識訊號是「stub 資料與斷言預期出自同一次測試撰寫」——沒有獨立於測試作者的行為出處。典型案例：前端把後端資料的 id 凍結在本地記錄裡，單元測試建立記錄時順手把對應資料餵進 stub，凍結 id 與 stub 資料恆常一致；真實世界裡讓 id 失效的是後端的合併操作，stub 沒有這個行為，於是「id 失效」這個狀態在測試裡永遠不會出現，功能全壞、測試全綠（<a href="/blog/testing/cases/stale-reference-stub-blindspot/" data-link-title="T.C5 凍結參照失效被 stub 遮蔽 — 測試全綠、功能全壞" data-link-desc="前端把後端資料的 id 凍結在本地記錄裡，後端的合併操作會重建資料、舊 id 全部失效 — 單元測試的 stub 由測試自己餵資料，永遠不會經歷「id 死亡」，bug 存在期間所有測試綠燈">T.C5</a>）。</p>
<h2 id="設計責任">設計責任</h2>
<p>判斷「stub 夠不夠」的問句是：如果把 stub 換成真實服務，斷言結果會不會改變？只驗證程式碼內部邏輯（狀態機轉換、錯誤處理分支）時，stub 夠用；驗證對象是「對後端行為的假設」本身時，stub 結構上驗證不出來，需要換成持有狀態、行為有實測出處的<a href="/blog/testing/knowledge-cards/semantic-fake-backend/" data-link-title="Semantic Fake Backend（語意級假後端）" data-link-desc="持有狀態、只固化已證實後端行為的測試假件；與由測試餵資料的 stub 以狀態歸屬和行為出處劃界">語意級假後端</a>。凡是前端持有後端 id 的地方，都要追問後端的哪些操作會讓這個 id 死亡，並把「id 失效」的劇本納入測試範圍——見<a href="/blog/testing/knowledge-cards/frozen-vs-live-reference/" data-link-title="Frozen vs Live Reference（凍結參照與活解析）" data-link-desc="下游持有上游 id 的兩種策略——寫死 id vs 查詢時動態解引用；上游重建後凍結參照失效，stub 對此結構性盲目">凍結參照與活解析</a>。</p>
]]></content:encoded></item><item><title>Test Double Taxonomy（Fowler 分類）</title><link>https://tarrragon.github.io/blog/testing/knowledge-cards/test-double-taxonomy/</link><pubDate>Mon, 20 Jul 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/testing/knowledge-cards/test-double-taxonomy/</guid><description>&lt;p>Test double 是所有「用來取代真實依賴」的測試替身的統稱，Martin Fowler 把它拆成五種角色：dummy（只填參數位置、從不被實際使用）、&lt;a href="https://tarrragon.github.io/blog/testing/knowledge-cards/stub/" data-link-title="Stub" data-link-desc="測試作者手動寫死回應資料的 test double：驗證的是假設成立時邏輯是否正確，假設本身錯誤時無法檢出">stub&lt;/a>（測試作者寫死固定回應）、spy（記錄呼叫過程、供事後檢查）、mock（預先設定期望、呼叫不符期望即失敗）、fake（有狀態、可運作的簡化實作）。五種角色的分野在「資料從哪裡來」與「驗證的是狀態還是互動」，不是隨口互換的同義詞。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>本模組已經在用三種角色、只是沒有集中命名：&lt;a href="https://tarrragon.github.io/blog/testing/knowledge-cards/mock-masking/" data-link-title="Mock Masking（Mock 遮蔽）" data-link-desc="mock 模擬 API 層但不模擬協議層，造成的結構性驗證盲區">mock 遮蔽&lt;/a>討論的 mock，忠實模擬 API 層的方法簽名與參數型別，驗證呼叫是否符合預期介面；&lt;a href="https://tarrragon.github.io/blog/testing/knowledge-cards/stub/" data-link-title="Stub" data-link-desc="測試作者手動寫死回應資料的 test double：驗證的是假設成立時邏輯是否正確，假設本身錯誤時無法檢出">stub&lt;/a>是測試作者逐條寫死的固定回應，驗證「假設成立時」邏輯是否正確；&lt;a href="https://tarrragon.github.io/blog/testing/knowledge-cards/semantic-fake-backend/" data-link-title="Semantic Fake Backend（語意級假後端）" data-link-desc="持有狀態、只固化已證實後端行為的測試假件；與由測試餵資料的 stub 以狀態歸屬和行為出處劃界">語意級假後端&lt;/a>對應 fake——有狀態、可運作、模擬已證實後端行為的簡化實作。三者的差異不是實作細節，是各自能檢出的 bug 類型不同：mock 檢出介面誤用，stub 檢不出假設本身的錯誤，fake 能讓假設在多服務接力中被真正驗證。&lt;/p>
&lt;h2 id="可觀察訊號與例子">可觀察訊號與例子&lt;/h2>
&lt;p>判斷一段程式碼裡的替身屬於哪個角色，看兩件事：資料是不是每條測試手動寫死的（是 → stub 或 dummy，取決於有沒有被實際讀取）、驗證是斷言最終狀態還是斷言呼叫發生過（前者偏 stub/fake，後者偏 spy/mock）。混用角色而不自知的訊號是：團隊說「這裡有 mock」，但程式碼裡是逐條寫死回應、沒有設定呼叫期望——用詞是 mock、行為是 stub，兩者的可測範圍不同，混用會讓覆蓋率的認知跟實際脫節。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>選角色的判準是「要驗證什麼」：只驗證程式碼內部邏輯、不涉及外部行為假設 → stub 或 dummy 就夠；要驗證「呼叫方式是否符合介面契約」→ mock；要驗證「假設成立時的多服務接力行為」，且假設本身有實測出處 → fake。角色選錯的典型後果是&lt;a href="https://tarrragon.github.io/blog/testing/knowledge-cards/frozen-vs-live-reference/" data-link-title="Frozen vs Live Reference（凍結參照與活解析）" data-link-desc="下游持有上游 id 的兩種策略——寫死 id vs 查詢時動態解引用；上游重建後凍結參照失效，stub 對此結構性盲目">凍結參照與活解析&lt;/a>描述的那類盲區——用 stub 的地方假設會變、卻用了固定回應，狀態變化不會在測試裡發生。跨層盲區（協議層、假設層）疊加時，測試綠燈能證明的範圍比表面上薄。&lt;/p></description><content:encoded><![CDATA[<p>Test double 是所有「用來取代真實依賴」的測試替身的統稱，Martin Fowler 把它拆成五種角色：dummy（只填參數位置、從不被實際使用）、<a href="/blog/testing/knowledge-cards/stub/" data-link-title="Stub" data-link-desc="測試作者手動寫死回應資料的 test double：驗證的是假設成立時邏輯是否正確，假設本身錯誤時無法檢出">stub</a>（測試作者寫死固定回應）、spy（記錄呼叫過程、供事後檢查）、mock（預先設定期望、呼叫不符期望即失敗）、fake（有狀態、可運作的簡化實作）。五種角色的分野在「資料從哪裡來」與「驗證的是狀態還是互動」，不是隨口互換的同義詞。</p>
<h2 id="概念位置">概念位置</h2>
<p>本模組已經在用三種角色、只是沒有集中命名：<a href="/blog/testing/knowledge-cards/mock-masking/" data-link-title="Mock Masking（Mock 遮蔽）" data-link-desc="mock 模擬 API 層但不模擬協議層，造成的結構性驗證盲區">mock 遮蔽</a>討論的 mock，忠實模擬 API 層的方法簽名與參數型別，驗證呼叫是否符合預期介面；<a href="/blog/testing/knowledge-cards/stub/" data-link-title="Stub" data-link-desc="測試作者手動寫死回應資料的 test double：驗證的是假設成立時邏輯是否正確，假設本身錯誤時無法檢出">stub</a>是測試作者逐條寫死的固定回應，驗證「假設成立時」邏輯是否正確；<a href="/blog/testing/knowledge-cards/semantic-fake-backend/" data-link-title="Semantic Fake Backend（語意級假後端）" data-link-desc="持有狀態、只固化已證實後端行為的測試假件；與由測試餵資料的 stub 以狀態歸屬和行為出處劃界">語意級假後端</a>對應 fake——有狀態、可運作、模擬已證實後端行為的簡化實作。三者的差異不是實作細節，是各自能檢出的 bug 類型不同：mock 檢出介面誤用，stub 檢不出假設本身的錯誤，fake 能讓假設在多服務接力中被真正驗證。</p>
<h2 id="可觀察訊號與例子">可觀察訊號與例子</h2>
<p>判斷一段程式碼裡的替身屬於哪個角色，看兩件事：資料是不是每條測試手動寫死的（是 → stub 或 dummy，取決於有沒有被實際讀取）、驗證是斷言最終狀態還是斷言呼叫發生過（前者偏 stub/fake，後者偏 spy/mock）。混用角色而不自知的訊號是：團隊說「這裡有 mock」，但程式碼裡是逐條寫死回應、沒有設定呼叫期望——用詞是 mock、行為是 stub，兩者的可測範圍不同，混用會讓覆蓋率的認知跟實際脫節。</p>
<h2 id="設計責任">設計責任</h2>
<p>選角色的判準是「要驗證什麼」：只驗證程式碼內部邏輯、不涉及外部行為假設 → stub 或 dummy 就夠；要驗證「呼叫方式是否符合介面契約」→ mock；要驗證「假設成立時的多服務接力行為」，且假設本身有實測出處 → fake。角色選錯的典型後果是<a href="/blog/testing/knowledge-cards/frozen-vs-live-reference/" data-link-title="Frozen vs Live Reference（凍結參照與活解析）" data-link-desc="下游持有上游 id 的兩種策略——寫死 id vs 查詢時動態解引用；上游重建後凍結參照失效，stub 對此結構性盲目">凍結參照與活解析</a>描述的那類盲區——用 stub 的地方假設會變、卻用了固定回應，狀態變化不會在測試裡發生。跨層盲區（協議層、假設層）疊加時，測試綠燈能證明的範圍比表面上薄。</p>
]]></content:encoded></item></channel></rss>