变异测试
入门
需要 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 将
- 仅运行覆盖变异代码的测试以加快处理速度。
- 尽可能多地缓存以加快后续运行的速度。
- 如果启用,则使用并行执行来并行运行多个测试以加快处理速度。
已测试与未测试的变异
在运行变异测试时,您将“主要”看到两种类型的变异:已测试和未测试的变异。
- 已测试的变异:这些是被您的测试套件检测到的变异。它们被认为是“已测试的”,因为您的测试能够捕获变异引入的更改。
例如,以下变异被认为是“已测试的”,因为测试套件能够检测到更改。
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 的变异。请注意,您需要提供与原始运行相同的选项。
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 的变异测试功能是一个强大的工具,可以提高测试套件的质量。在下一章中,我们将解释如何使用快照来测试您的代码:快照测试