突變操作
突變測試通過靈活可配置的突變操作集來從你的代碼中編譯產(chǎn)生新的代碼。
例如CONDITIONALS_BOUNDARY_MUTATOR
改變條件邊界突變會從源代碼中生成下面的語句。
if ( i >= 0 ) {
return "foo";
} else {
return "bar";
}
與其對應(yīng)的語句是
if ( i > 0 ) {
return "foo";
} else {
return "bar";
}
PIT定義了各式各樣從源代碼中突變的方法,包括移除方法調(diào)用、反置邏輯條件、更改返回值等等韵洋。
PIT的運行需要編譯器在字節(jié)碼中提供以下調(diào)試信息:
- 代碼行號
- 源代碼文件名
絕大部分構(gòu)建系統(tǒng)捧颅,如 maven 默認(rèn)就提供了這些信息景图。
突變
PIT通過應(yīng)用突變操作符將生成許多(可能是非常大的數(shù)量)突變體。每一個突變Java類包含一個突變(或錯誤)碉哑,使它們的行為不同于未突變的類挚币。
PIT將這些突變后的類執(zhí)行你的單元測試,如果突變可以被殺死(突變后不滿足測試用例)扣典,則證明本次測試是有效的妆毕。
等價突變
通過改變代碼條件來改變執(zhí)行邏輯并非那么簡單,某些情況下的突變完全等價于未突變之前的邏輯贮尖。
等價突變產(chǎn)生的原因有兩點:
突變的結(jié)果恰好與突變前的行為完全一致笛粘。
例如,下面兩份代碼是邏輯完全等價的:
int i = 2;
if ( i >= 1 ) {
return "foo";
}
//...
int i = 2;
if ( i > 1 ) {
return "foo";
}
突變前后雖然有了不同的行為湿硝,但是測試用例并沒有覆蓋到突變產(chǎn)生的非正常結(jié)果薪前。
另一個比較常見的等價突變的示例是DEBUG或者日志,PIT排除了common logging等日志組件的突變生成关斜,也可以通過配置來過濾日志組件序六。
測試執(zhí)行
PIT會根據(jù)修改后的代碼自動運行單元測試。在運行測試之前蚤吹,PIT對測試執(zhí)行傳統(tǒng)的行覆蓋率分析例诀,然后使用這些數(shù)據(jù)和測試的時間來選擇一組測試用例,這些測試用例的目標(biāo)是修改后的代碼裁着。然后使用這些及測試之后的數(shù)據(jù)來選擇測試用例執(zhí)行一組突變代碼繁涂。
這種執(zhí)行機制也是PIT的執(zhí)行速度遠(yuǎn)快于其它類似的突變測試系統(tǒng)的原因(如Jester 、 Jumble)二驰,這也是PIT能夠?qū)y試提升到全部代碼扔罪,而不是僅僅測試單個類的技術(shù)實現(xiàn)。
突變測試報告的輸出結(jié)果集解釋如下:
- Killed : 突變被殺死桶雀,代表突變的代碼不滿足測試用例
- Lived : 突變存活矿酵,意味著等價突變或者突變改變的邏輯,但是仍符合測試用例矗积,此時的代碼可能是有問題的全肮!
- No coverage 代碼沒有被覆蓋到,和存活一樣棘捣,這個測試可能也是有問題的辜腺。
- Non viable : 不可行的突變是JVM無法加載的,因為字節(jié)碼在某種程度上是無效的。PIT試圖將它所產(chǎn)生的不可存活突變的數(shù)量降到最低评疗。
- Timed Out : 如果突變導(dǎo)致無限循環(huán)测砂,例如從for循環(huán)中的計數(shù)器中刪除增量,則可能超時百匆。
- Memory error : 內(nèi)存錯誤可能由于增加系統(tǒng)使用的內(nèi)存數(shù)量的突變而發(fā)生砌些,也可能是在出現(xiàn)突變時重復(fù)運行測試所需的額外內(nèi)存開銷的結(jié)果。如果您看到大量內(nèi)存錯誤加匈,請考慮為測試配置更多的堆和permgen空間存璃。
- Run error : 運行錯誤意味著在嘗試測試突變時出錯了。某些類型的不可存活突變目前可能導(dǎo)致運行錯誤矩动。如果您看到大量的運行錯誤,這可能是出錯的跡象释漆。
在正常情況下悲没,您應(yīng)該不會看到任何不可行的突變或運行錯誤。