基于属性的测试在 PHP 中:比您写代码更快地找出漏洞

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

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

开始课程

引言:为什么您的测试在撒谎,而您却相信它们

\n\n

想象一下:您编写了 50 个单元测试,覆盖了所有 if-else 分支,通过了 CI 流水线——全部通过。一周后,生产环境因错误 "Division by zero" 而崩溃。怎么会?测试明明都通过了!熟悉吗?

\n\n

经典单元测试(示例性的,带有具体数字)的问题在于,它们只检查您已经知道的内容。您选择那些您认为会导致错误的参数。但程序员的思维是可预测的:我们都会写 testDivideByZero,却忘记了 testDivideByNegativeZero 或 testDivideByVeryLargeFloat。

\n\n

这时,基于属性的测试在 PHP 中 登场了——这种方法不检查具体的场景,而是针对数千个随机输入数据来证明您代码的属性。今天,我们将探讨如何使用 Eris 和 PHPUnit 将此方法集成到您的 PHP 项目中。您将学习如何发现普通测试遗漏的 bug,并理解这并非魔法,而是工程学。

\n\n

什么是基于属性的测试?(以及为什么它不是随机测试)

\n\n

一个普通的单元测试看起来像这样:assert(2 + 2 === 4)。您检查一个具体的断言。基于属性的测试 (PBT) 则不同:您阐述一个属性——一个对于任何有效输入数据都必须为真的断言。

\n\n

加法函数属性的示例:

\n
    \n
  • 交换律: a + b === b + a 对于任何数字都成立。
  • \n
  • 幂等性: 重复应用函数不会改变结果。
  • \n
  • 不变性: 结果始终为正(如果这是属性的话)。
  • \n
\n\n

PBT 生成数百个随机值(包括边界值、零值、负值、NaN),并检查属性是否被违反。如果找到反例,测试将失败,并显示导致失败的具体输入数据。您得到的不仅仅是“测试失败”,而是一个可重现的 bug。

\n\n

在 PHP 中,由于动态类型,这项技术尤其有用:您永远不知道实际传入函数的类型是什么。

\n\n

PHP 中基于属性测试的工具

\n\n

主要工具是 Eris 库(github.com/giorgiosironi/eris)。它与 PHPUnit 集成,并提供随机数据生成器。替代方案是 PHPUnit 10+ 的内置功能(但尚不成熟)。我们将使用 Eris,因为它是一个成熟且有文档的工具。

\n\n

通过 Composer 安装:

\n
composer require --dev giorgiosironi/eris
\n\n

同时,Faker 对于生成复杂结构(电子邮件、姓名、地址)也很有用。

\n\n
composer require --dev fakerphp/faker
\n\n

编写第一个基于属性的测试:破坏排序函数

\n\n

假设我们有一个使用快速排序的函数 sortArray()。我们编写一个属性:“排序后的数组不应包含比前一个元素更小的元素”。

\n\n
use Eris\\TestTrait;\nuse Eris\\Generator\\IntegerGenerator;\nuse PHPUnit\\Framework\\TestCase;\n\nclass SortTest extends TestCase\n{\n    use TestTrait;\n\n    public function testSortedArrayProperty(): void\n    {\n        $this->forAll(\n            Generator\\seq(Generator\\int())\n        )->then(function (array $input) {\n            $sorted = sortArray($input);\n            for ($i = 0; $i < count($sorted) - 1; $i++) {\n                $this->assertLessThanOrEqual(\n                    $sorted[$i + 1],\n                    $sorted[$i],\n                    "数组未排序: " . json_encode($sorted)\n                );\n            }\n        });\n    }\n}\n
\n\n

运行

博客

书籍推荐