<?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-Quality on Tarragon</title><link>https://tarrragon.github.io/blog/tags/test-quality/</link><description>Recent content in Test-Quality on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Mon, 24 Aug 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/test-quality/index.xml" rel="self" type="application/rss+xml"/><item><title>Mutation Testing（突變測試）</title><link>https://tarrragon.github.io/blog/testing/knowledge-cards/mutation-testing/</link><pubDate>Mon, 24 Aug 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/testing/knowledge-cards/mutation-testing/</guid><description>&lt;p>Mutation testing 對被測程式注入單點的行為改變（把 &lt;code>&amp;gt;&lt;/code> 換成 &lt;code>&amp;gt;=&lt;/code>、把回傳值換成常數、刪掉一次呼叫），再看測試套件會不會因此變紅。變紅稱為殺死該突變，維持全綠稱為存活；存活的突變指出一件具體的事——程式在那個位置改掉行為，這套測試不會發現。它量的是測試的&lt;strong>偵測能力&lt;/strong>，而行覆蓋率量的是&lt;strong>執行足跡&lt;/strong>，兩者的差距正是斷言可以完全不寫卻仍有 100% 覆蓋率的那段空間。它不提供判準，它檢查既有 &lt;a href="https://tarrragon.github.io/blog/testing/knowledge-cards/test-oracle/" data-link-title="Test Oracle（測試判準來源）" data-link-desc="一個測試憑什麼判定通過或失敗說不清楚、或被測對象沒有可逐例算出的正確答案時，用來定位判準的來源，以及每一種來源的射程">oracle&lt;/a> 的密度。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>突變分數（被殺死的突變數除以有效突變數）是覆蓋率的替代指標——字面上的空斷言灌不高它，而錄製式斷言這類低思考量的路徑仍然可以，射程在&lt;a href="https://tarrragon.github.io/blog/testing/06-agent-authored-code/coverage-to-mutation-gate/" data-link-title="品質閘門的更替：覆蓋率、突變分數與各自的射程" data-link-desc="測試數量大幅增加而信心沒有跟著上升、或要為生成的測試挑一個不會被灌水的門檻時，用來決定量什麼，以及那個量的盲區在哪裡">品質閘門的更替&lt;/a>。它跟 &lt;a href="https://tarrragon.github.io/blog/testing/knowledge-cards/test-oracle/" data-link-title="Test Oracle（測試判準來源）" data-link-desc="一個測試憑什麼判定通過或失敗說不清楚、或被測對象沒有可逐例算出的正確答案時，用來定位判準的來源，以及每一種來源的射程">test oracle&lt;/a> 的分工是：oracle 決定判準從哪來，突變分數量既有判準的密度。覆蓋率之所以長期被當成閘門，前提是「執行到了通常也就斷言了」——這個前提在斷言由人一行一行寫的年代大致成立，在斷言可以整批產生之後不再成立。用突變分數當閘門的理由是它無法用空斷言達成：要殺死突變，斷言必須真的檢查到那個行為。&lt;/p>
&lt;p>它的成本結構跟其他測試層不同：每一個突變都要重跑一次相關測試，所以執行時間與突變數乘上測試時間成正比。壓低成本的三種手法，以及分數自己的至少四條便宜達成路徑（兩組互不對應），在&lt;a href="https://tarrragon.github.io/blog/testing/06-agent-authored-code/coverage-to-mutation-gate/" data-link-title="品質閘門的更替：覆蓋率、突變分數與各自的射程" data-link-desc="測試數量大幅增加而信心沒有跟著上升、或要為生成的測試挑一個不會被灌水的門檻時，用來決定量什麼，以及那個量的盲區在哪裡">品質閘門的更替&lt;/a>那一章。&lt;/p>
&lt;h2 id="可觀察訊號與例子">可觀察訊號與例子&lt;/h2>
&lt;p>覆蓋率維持在 90% 以上，而漏出去的 bug 落在有被覆蓋的程式碼裡——這組數字同時出現時，斷言多半只檢查到「有沒有跑完」，而突變分數會立刻把落差顯示出來。&lt;/p>
&lt;p>存活的突變分成兩類，處理方式相反：真的漏洞要補斷言，等價突變（改寫之後行為完全相同）是工具的限制，要標記排除。為等價突變硬補的斷言會綁在實作寫法上，反過來傷害重構安全性。兩者判不開時的處置紀律（時間盒、預設歸類、排除清單覆核）在&lt;a href="https://tarrragon.github.io/blog/testing/06-agent-authored-code/coverage-to-mutation-gate/" data-link-title="品質閘門的更替：覆蓋率、突變分數與各自的射程" data-link-desc="測試數量大幅增加而信心沒有跟著上升、或要為生成的測試挑一個不會被灌水的門檻時，用來決定量什麼，以及那個量的盲區在哪裡">品質閘門的更替&lt;/a>。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>引入的順序是先有可信的 oracle、再談突變分數。突變是對照著程式&lt;strong>現在的樣子&lt;/strong>產生的，因此它守得住「斷言不夠密」，守不住「這段程式一開始就實作錯了需求」——把危險判定順序寫反的程式碼跑突變測試，長出來的斷言會把那個順序釘得更牢（機制推論，非實驗觀察）。這個限制在測試與實作&lt;a href="https://tarrragon.github.io/blog/testing/knowledge-cards/test-provenance/" data-link-title="Test Provenance（測試出處）" data-link-desc="測試與被測實作由同一來源產出時（同一次生成、同一輪對話、同一個人同時寫），用來判斷這組測試還剩下多少驗證力">出處不獨立&lt;/a>時特別要緊，因為那正是規格層錯誤最容易發生的情境。&lt;/p>
&lt;p>它也不會提出新的問題。突變生出來的斷言全部瞄準運算子與回傳值層級的翻轉，屬於 &lt;a href="https://tarrragon.github.io/blog/testing/knowledge-cards/testing-vs-checking/" data-link-title="Testing vs Checking（探究與檢查）" data-link-desc="斷言可以被整批產生之後，用來區分哪些驗證工作交得出去給機器、哪些需要人的判斷才成立">checking 的補完&lt;/a>；產品有哪些情境值得驗證，仍然要由人設計。&lt;/p></description><content:encoded><![CDATA[<p>Mutation testing 對被測程式注入單點的行為改變（把 <code>&gt;</code> 換成 <code>&gt;=</code>、把回傳值換成常數、刪掉一次呼叫），再看測試套件會不會因此變紅。變紅稱為殺死該突變，維持全綠稱為存活；存活的突變指出一件具體的事——程式在那個位置改掉行為，這套測試不會發現。它量的是測試的<strong>偵測能力</strong>，而行覆蓋率量的是<strong>執行足跡</strong>，兩者的差距正是斷言可以完全不寫卻仍有 100% 覆蓋率的那段空間。它不提供判準，它檢查既有 <a href="/blog/testing/knowledge-cards/test-oracle/" data-link-title="Test Oracle（測試判準來源）" data-link-desc="一個測試憑什麼判定通過或失敗說不清楚、或被測對象沒有可逐例算出的正確答案時，用來定位判準的來源，以及每一種來源的射程">oracle</a> 的密度。</p>
<h2 id="概念位置">概念位置</h2>
<p>突變分數（被殺死的突變數除以有效突變數）是覆蓋率的替代指標——字面上的空斷言灌不高它，而錄製式斷言這類低思考量的路徑仍然可以，射程在<a href="/blog/testing/06-agent-authored-code/coverage-to-mutation-gate/" data-link-title="品質閘門的更替：覆蓋率、突變分數與各自的射程" data-link-desc="測試數量大幅增加而信心沒有跟著上升、或要為生成的測試挑一個不會被灌水的門檻時，用來決定量什麼，以及那個量的盲區在哪裡">品質閘門的更替</a>。它跟 <a href="/blog/testing/knowledge-cards/test-oracle/" data-link-title="Test Oracle（測試判準來源）" data-link-desc="一個測試憑什麼判定通過或失敗說不清楚、或被測對象沒有可逐例算出的正確答案時，用來定位判準的來源，以及每一種來源的射程">test oracle</a> 的分工是：oracle 決定判準從哪來，突變分數量既有判準的密度。覆蓋率之所以長期被當成閘門，前提是「執行到了通常也就斷言了」——這個前提在斷言由人一行一行寫的年代大致成立，在斷言可以整批產生之後不再成立。用突變分數當閘門的理由是它無法用空斷言達成：要殺死突變，斷言必須真的檢查到那個行為。</p>
<p>它的成本結構跟其他測試層不同：每一個突變都要重跑一次相關測試，所以執行時間與突變數乘上測試時間成正比。壓低成本的三種手法，以及分數自己的至少四條便宜達成路徑（兩組互不對應），在<a href="/blog/testing/06-agent-authored-code/coverage-to-mutation-gate/" data-link-title="品質閘門的更替：覆蓋率、突變分數與各自的射程" data-link-desc="測試數量大幅增加而信心沒有跟著上升、或要為生成的測試挑一個不會被灌水的門檻時，用來決定量什麼，以及那個量的盲區在哪裡">品質閘門的更替</a>那一章。</p>
<h2 id="可觀察訊號與例子">可觀察訊號與例子</h2>
<p>覆蓋率維持在 90% 以上，而漏出去的 bug 落在有被覆蓋的程式碼裡——這組數字同時出現時，斷言多半只檢查到「有沒有跑完」，而突變分數會立刻把落差顯示出來。</p>
<p>存活的突變分成兩類，處理方式相反：真的漏洞要補斷言，等價突變（改寫之後行為完全相同）是工具的限制，要標記排除。為等價突變硬補的斷言會綁在實作寫法上，反過來傷害重構安全性。兩者判不開時的處置紀律（時間盒、預設歸類、排除清單覆核）在<a href="/blog/testing/06-agent-authored-code/coverage-to-mutation-gate/" data-link-title="品質閘門的更替：覆蓋率、突變分數與各自的射程" data-link-desc="測試數量大幅增加而信心沒有跟著上升、或要為生成的測試挑一個不會被灌水的門檻時，用來決定量什麼，以及那個量的盲區在哪裡">品質閘門的更替</a>。</p>
<h2 id="設計責任">設計責任</h2>
<p>引入的順序是先有可信的 oracle、再談突變分數。突變是對照著程式<strong>現在的樣子</strong>產生的，因此它守得住「斷言不夠密」，守不住「這段程式一開始就實作錯了需求」——把危險判定順序寫反的程式碼跑突變測試，長出來的斷言會把那個順序釘得更牢（機制推論，非實驗觀察）。這個限制在測試與實作<a href="/blog/testing/knowledge-cards/test-provenance/" data-link-title="Test Provenance（測試出處）" data-link-desc="測試與被測實作由同一來源產出時（同一次生成、同一輪對話、同一個人同時寫），用來判斷這組測試還剩下多少驗證力">出處不獨立</a>時特別要緊，因為那正是規格層錯誤最容易發生的情境。</p>
<p>它也不會提出新的問題。突變生出來的斷言全部瞄準運算子與回傳值層級的翻轉，屬於 <a href="/blog/testing/knowledge-cards/testing-vs-checking/" data-link-title="Testing vs Checking（探究與檢查）" data-link-desc="斷言可以被整批產生之後，用來區分哪些驗證工作交得出去給機器、哪些需要人的判斷才成立">checking 的補完</a>；產品有哪些情境值得驗證，仍然要由人設計。</p>
]]></content:encoded></item></channel></rss>