Mutation Testing(突變測試)
Mutation testing 對被測程式注入單點的行為改變(把 > 換成 >=、把回傳值換成常數、刪掉一次呼叫),再看測試套件會不會因此變紅。變紅稱為殺死該突變,維持全綠稱為存活;存活的突變指出一件具體的事——程式在那個位置改掉行為,這套測試不會發現。它量的是測試的偵測能力,而行覆蓋率量的是執行足跡,兩者的差距正是斷言可以完全不寫卻仍有 100% 覆蓋率的那段空間。它不提供判準,它檢查既有 oracle 的密度。
概念位置
突變分數(被殺死的突變數除以有效突變數)是覆蓋率的替代指標——字面上的空斷言灌不高它,而錄製式斷言這類低思考量的路徑仍然可以,射程在品質閘門的更替。它跟 test oracle 的分工是:oracle 決定判準從哪來,突變分數量既有判準的密度。覆蓋率之所以長期被當成閘門,前提是「執行到了通常也就斷言了」——這個前提在斷言由人一行一行寫的年代大致成立,在斷言可以整批產生之後不再成立。用突變分數當閘門的理由是它無法用空斷言達成:要殺死突變,斷言必須真的檢查到那個行為。
它的成本結構跟其他測試層不同:每一個突變都要重跑一次相關測試,所以執行時間與突變數乘上測試時間成正比。壓低成本的三種手法,以及分數自己的至少四條便宜達成路徑(兩組互不對應),在品質閘門的更替那一章。
可觀察訊號與例子
覆蓋率維持在 90% 以上,而漏出去的 bug 落在有被覆蓋的程式碼裡——這組數字同時出現時,斷言多半只檢查到「有沒有跑完」,而突變分數會立刻把落差顯示出來。
存活的突變分成兩類,處理方式相反:真的漏洞要補斷言,等價突變(改寫之後行為完全相同)是工具的限制,要標記排除。為等價突變硬補的斷言會綁在實作寫法上,反過來傷害重構安全性。兩者判不開時的處置紀律(時間盒、預設歸類、排除清單覆核)在品質閘門的更替。
設計責任
引入的順序是先有可信的 oracle、再談突變分數。突變是對照著程式現在的樣子產生的,因此它守得住「斷言不夠密」,守不住「這段程式一開始就實作錯了需求」——把危險判定順序寫反的程式碼跑突變測試,長出來的斷言會把那個順序釘得更牢(機制推論,非實驗觀察)。這個限制在測試與實作出處不獨立時特別要緊,因為那正是規格層錯誤最容易發生的情境。
它也不會提出新的問題。突變生出來的斷言全部瞄準運算子與回傳值層級的翻轉,屬於 checking 的補完;產品有哪些情境值得驗證,仍然要由人設計。