PHP中的变异测试:如何编写真正能捕捉错误的测试

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:为什么100%的代码覆盖率是一种错觉?

\n

您是否曾为PHPUnit报告中写着“Code Coverage: 98%”而感到自豪?然后发布版本,却在生产环境出现严重错误。熟悉吗?问题在于,经典的覆盖率(行/语句覆盖率)只衡量哪些代码行被执行了。它并不检查您的测试是否真正验证了逻辑。

\n

想象一下,您编写了一个测试来检查函数是否返回true。而该函数即使被破坏也总是返回true。测试会通过,覆盖率是100%,但您无法捕捉到错误。正是在这里,PHP中的变异测试——一种颠覆传统测试观念的技术——发挥了作用。

\n

在本文中,我们将通过真实的PHP代码示例,详细讲解什么是变异测试、它是如何工作的,以及如何使用Infection库将其集成到您的项目中。

\n\n

什么是变异测试?(以及为什么这不是魔法)

\n

Mutation testing(或变异测试)是一种评估测试质量的方法,它通过对源代码进行微小且故意的更改(变异)来实现。如果更改后测试失败——变异体被杀死,测试有效。如果测试仍然通过——变异体存活,意味着您的测试没有验证任何东西。

\n

一个简单的类比:您是保安,您的测试是摄像头。变异测试将“入侵者”(变异体)放入大楼。如果摄像头(测试)发现了入侵者——它有效。如果入侵者自由走动——摄像头毫无用处。

\n

在PHP中,实现这一目标的主要工具是Infection——一个对每个变异体运行PHPUnit并分析结果的库。

\n\n

实际中是什么样子?

\n

假设您有一个简单的计算器类:

\n
<?php\nclass Calculator\n{\n    public function add(int $a, int $b): int\n    {\n        return $a + $b;\n    }\n}
\n

以及它的测试:

\n
<?php\nuse PHPUnit\\Framework\\TestCase;\n\nclass CalculatorTest extends TestCase\n{\n    public function testAdd()\n    {\n        $calc = new Calculator();\n        $result = $calc->add(2, 3);\n        $this->assertEquals(5, $result);\n    }\n}
\n

Infection工具会创建一个变异体,将+替换为-:

\n
return $a - $b;
\n

然后运行测试。如果测试失败——变异体被杀死。如果测试通过(它会通过,因为您只检查了2+3,而不是2-3)——变异体存活。这是一个信号:您的测试不够好。

\n\n

安装Infection并首次运行

\n

首先,通过Composer安装Infection:

\n
composer require --dev infection/infection
\n

然后创建配置文件infection.json(或运行vendor/bin/infection进行交互式设置)。

\n

最小配置示例:

\n
{\n    "source": {\n        "directories": [\n            "src"\n        ]\n    },\n    "phpUnit": {\n        "configDir": "."\n    },\n    "mutators": {\n        "@default": true\n    }\n}
\n

运行变异测试:

\n
vendor/bin/infection --min-msi=100
\n

其中--min-msi=100意味着我们希望100%的变异体被杀死(理想但很少达到的指标)。Infection会生成HTML格式的报告(infection.html文件夹),其中存活的变异体将以红色标记。

\n\n

PHP中的变异类型(示例说明)

\n

Infection支持数十种变异器。以下是您会遇到的最常见的几种:

\n\n

1. 数学运算符

\n

将+替换为-,*替换为/,%替换为*等。

\n
// 原始代码\nreturn $price * $quantity;\n\n// 变异体\nreturn $price / $quantity;

博客

书籍推荐