突變測試
入門
需要 XDebug 3.0+ 或 PCOV。
突變測試是一種創新的新技術,可以對程式碼引入小變更(突變),然後看看測試能不能偵測到這些突變。這可以確保你徹底測試應用程式,不僅達到程式碼涵蓋率,還更深入探討測試的實際品質。這是找出測試套件弱點並提升品質的好方法。
要開始執行突變測試,請前往測試檔案,然後明確指出測試涵蓋程式碼的哪個部分,可使用 covers()
函式或 mutates
函式。
1covers(TodoController::class); // or mutates(TodoController::class);2 3it('list todos', function () {4 $this->getJson('/todos')->assertStatus(200);5});
在突變測試中,covers
和 mutates
函式是相同的。但是,covers
也會影響程式碼涵蓋率報告。如果提供,則它會篩選程式碼涵蓋率報告,只包含從參考程式碼部分執行的程式碼。
接下來,使用 --mutate
選項執行 Pest PHP 以開始突變測試。最好搭配 --parallel
選項使用,以加速處理流程。
1./vendor/bin/pest --mutate2# or in parallel...3./vendor/bin/pest --mutate --parallel
然後 Pest 會針對「突變」程式碼重新執行測試,並查看測試是否依然通過。如果測試針對某個突變依然通過,表示該測試並未涵蓋程式碼的特定部分。因此,Pest 會輸出該突變和程式碼的差異。
1UNTESTED app/Http/TodoController.php > Line 44: ReturnValue - ID: 76d17ad63bb7c307 2 3class TodoController { 4 public function index(): array 5 { 6 // pest detected that this code is untested because 7 // the test is not covering the return value 8- return Todo::all()->toArray(); 9+ return [];10 }11}12 13 Mutations: 1 untested14 Score: 33.44%
找出未測試的程式碼後,你可以撰寫額外的測試來涵蓋該程式碼。
1covers(TodoController::class);2 3it('list todos', function () {4+ Todo::factory()->create(['name' => 'Buy milk']);5 6- $this->getJson('/todos')->assertStatus(200);7+ $this->getJson('/todos')->assertStatus(200)->assertJson([['name' => 'Buy milk']]);8});
然後,你可以使用 --mutate
選項重新執行 Pest,看看突變是否已經「通過測試」並獲得涵蓋。
1Mutations: 1 tested2Score: 100.00%
突變分數越高,測試套件就越好。突變分數 100% 表示所有突變都已經「通過測試」,這是突變測試的目標。
現在,如果你看到「未測試」或「未涵蓋」突變,或突變分數低於 100%,通常表示你有遺漏測試或測試沒有涵蓋所有邊界條件。
我們的外掛已深入整合 Pest PHP。因此,每次引入突變時,Pest PHP 會
- 僅執行涵蓋變異程式碼的測試,以加快處理時間。
- 盡量快取,以加快後續執行時處理時間。
- 如果啟用,請使用平行執行以 paralel執行多個測試,以加快處理時間。
已測試 vs. 未測試的變異
執行變異測試時,你將「主要」看到兩個類型的變異:已測試和未測試的變異。
- 已測試的變異:這些變異會被你的測試組件檢測到。它們被視為「已測試」,因為你的測試能夠捕捉到變異導入的變更。
例如,以下變異被視為「已測試」,因為測試組件能夠檢測到變更。
1class TodoController 2{ 3 public function index(): array 4 { 5- return Todo::all()->toArray(); 6+ return []; 7 } 8} 9 10it('list todos', function () {11 Todo::factory()->create(['name' => 'Buy milk']);12 13 // this fails because the mutation changed the return value, proving that the test is working and testing the return value...14 $this->getJson('/todos')->assertStatus(200)->assertJsonContains([15 ['name' => 'Buy milk'],16 ]);17});
- 未測試的變異:這些變異不會被你的測試組件檢測到。它們被視為「未測試」,因為你的測試無法捕捉到變異導入的變更。
例如,以下變異被視為「未測試」,因為測試組件無法檢測到變更。
1class TodoController 2{ 3 public function index(): array 4 { 5- return Todo::all()->toArray(); 6+ return []; 7 } 8} 9 10it('list todos', function () {11 Todo::factory()->create(['name' => 'Buy milk']);12 13 // this test still passes even though the return value was changed by the mutation...14 $this->getJson('/todos')->assertStatus(200);15});
變更回傳值只是許多可能的變異之一。一般來說,變異可以是回傳值變更、方法呼叫變更、方法參數變更等等。
最低臨界值強制執行
為確保全面的測試和維持測試品質,設定變異測試結果的最低臨界值至關重要。在 Pest 中,你可以使用 --mutation
和 --min
選項來定義變異測試評分結果的最低臨界值。如果未達到指定的臨界值,Pest 將回報失敗。
1./vendor/bin/pest --mutate --min=40

選項和修改器
執行變異測試時,可以使用以下選項和修改器。
@pest-mutate-ignore
在產生變異時,忽略給定的程式碼列。
1public function rules(): array2{3 return [4 'name' => 'required',5 'email' => 'required|email', // @pest-mutate-ignore6 ];7}
--id
僅執行 ID 為給定 ID 的變異。請注意,你必須提供與原始執行相同的選項。
1./vendor/bin/pest --mutate --id=ecb35ab30ffd3491
--everything
為專案所有類別產生變異,繞過 covers()
方法。此選擇非常消耗資源,應與 --covered-only
選項合併使用。
1./vendor/bin/pest --everything --parallel --covered-only
理想情況下,你也可以合併 --parallel
選項以加快處理時間。
--covered-only
僅在測試涵蓋的程式碼列中產生變異。
1./vendor/bin/pest --mutate --covered-only
--bail
在執行第一次未測試或未覆蓋的異動時停止異動測試執行。
1./vendor/bin/pest --mutate --bail
--class
為給定的類別產生異動。例如:--class=App\Models
。
1 2./vendor/bin/pest --mutate --class=App\Models
--ignore
在產生異動時忽略給定的類別。例如:--ignore=App\Http\Requests
。
1./vendor/bin/pest --mutate --ignore=App\Http\Requests
--clear-cache
清除異動快取並從頭執行異動測試。
1./vendor/bin/pest --mutate --clear-cache
--no-cache
在不使用快取的異動的情況下執行異動測試。
1./vendor/bin/pest --mutate --no-cache
--ignore-min-score-on-zero-mutations
當沒有異動時忽略最小分數需求。
1./vendor/bin/pest --mutate --min=80 --ignore-min-score-on-zero-mutations
--profile
將前十名最慢異動輸出至標準輸出。
1./vendor/bin/pest --mutate --profile
--retry
首先執行未測試或未覆蓋的異動,並在第一次錯誤或失敗時停止執行。
1./vendor/bin/pest --mutate --retry
--stop-on-uncovered
在執行第一次未測試的異動時停止異動測試執行。
1./vendor/bin/pest --mutate --stop-on-uncovered
--stop-on-untested
在執行第一次未測試的異動時停止異動測試執行。
1./vendor/bin/pest --mutate --stop-on-untested
如您所見,Pest PHP 的異動測試功能是一項強大的工具,可提升測試套件品質。在下一章節中,我們將說明如何使用快照來測試您的程式碼:快照測試