大维-利达门 / phpstan 规则测试助手
一个库,可以帮助更轻松地测试 PHPStan 规则
Requires
- php: ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0
- phpstan/phpstan: ^1.6
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.7
- php-parallel-lint/php-parallel-lint: ^1.3.2
- phpunit/phpunit: ^9.0
README
此库为 PHPStan 的自定义规则测试框架提供了一些改进。
该库提供了 AbstractRuleTestCase,它扩展了 PHPStan 的 RuleTestCase
。
它提供了一种更简单的方式来编写自定义规则的测试。具体来说
- 无需在测试代码中指定行号。
- 只需指定一次预期的错误信息。
改进 1:测试中不再需要行号
最小测试用例指定要测试的规则和至少一个测试。每个测试都必须调用 assertIssuesReported
方法,该方法接受一个或多个固定文件路径。
测试代码
use DaveLiddament\PhpstanRuleTestHelper\AbstractRuleTestCase; class CallableFromRuleTest extends AbstractRuleTestCase { protected function getRule(): Rule { return new CallableFromRule($this->createReflectionProvider()); } public function testAllowedCall(): void { $this->assertIssuesReported(__DIR__ . '/Fixtures/SomeCode.php'); } }
固定文件包含预期的错误信息。
固定文件
class SomeCode { public function go(): void { $item = new Item("hello"); $item->updateName("world"); // ERROR Can not call method } }
任何包含 // ERROR
的行都被视为应由规则捕获的问题。 // ERROR
后面的文本是预期的错误信息。
使用这种方法,您不需要确定错误的行号。当您更新固定文件时,您不再需要更新测试中的所有行号。
改进 2:一次指定预期的错误信息
您通常为每个违规行为编写相同的错误信息。为了解决这个问题,使用 getErrorFormatter
方法来指定错误信息。
测试代码
use DaveLiddament\PhpstanRuleTestHelper\AbstractRuleTestCase; class CallableFromRuleTest extends AbstractRuleTestCase { // `getRule` and `testAllowedCall` methods are as above and are omitted for brevity protected function getErrorFormatter(): string { return "Can not call method"; } }
固定文件简化了,因为没有必要指定错误信息。任何期望出现错误的行都需要以 // ERROR
结尾,预期的错误信息从 getErrorFormatter
方法中获取。
固定文件
class SomeCode { public function go(): void { $item = new Item("hello"); $item->updateName("world"); // ERROR } public function go2(): void { $item = new Item("hello"); $item->remove(); // ERROR } }
预期的错误信息如下
- 第 6 行:
无法调用方法
- 第 12 行:
无法调用方法
这种方法的好处是避免了错误信息文本的重复。任何对错误信息的更改只需在测试用例的一个地方进行。
为错误信息添加上下文
好的错误信息需要上下文。上下文添加到固定文件中的 // ERROR
之后。可以通过 |
字符将多个上下文分开。
测试代码
use DaveLiddament\PhpstanRuleTestHelper\AbstractRuleTestCase; class CallableFromRuleTest extends AbstractRuleTestCase { // `getRule` and `testAllowedCall` methods are as above and are omitted for brevity protected function getErrorFormatter(): string { return "Can not call {0} from within class {1}"; } }
固定文件
class SomeCode { public function go(): void { $item = new Item("hello"); $item->updateName("world"); // ERROR Item::updateName|SomeCode } public function go2(): void { $item = new Item("hello"); $item->remove(); // ERROR Item::remove|SomeCode } }
预期的错误信息如下
- 第 6 行:
无法在类 SomeCode 中从 Item::updateName 调用方法
- 第 11 行:
无法在类 SomeCode 中从 Item::remove 调用方法
更灵活的错误信息
如果您需要更灵活的错误信息,可以返回一个实现 ErrorMessageFormatter
接口 的对象。
在下面的示例中,信息根据错误上下文部分的数目而变化。
注意:这是一个虚构的例子,但它展示了您如何使用 ErrorMessageFormatter
来创建更灵活的错误信息。
use DaveLiddament\PhpstanRuleTestHelper\AbstractRuleTestCase; class CallableFromRuleTest extends AbstractRuleTestCase { // `getRule` and `testAllowedCall` methods omitted are as above and are for brevity protected function getErrorFormatter(): ErrorMessageFormatter { new class() extends ErrorMessageFormatter { public function getErrorMessage(string $errorContext): string { $parts = $this->getErrorMessageAsParts($errorContext); $calledFrom = count($parts) === 2 ? 'class '.$parts[1] : 'outside an object'; return sprintf('Can not call %s from %s', $parts[0], $calledFrom); } }; } }
固定文件
class SomeCode { public function go(): void { $item = new Item("hello"); $item->updateName("world"); // ERROR Item::updateName|SomeCode } } $item = new Item("hello"); $item->remove(); // ERROR Item::remove
预期的错误信息如下
- 第 6 行:
无法从类 SomeCode 调用 Item::updateName
- 第 11 行:
无法从对象外部调用 Item::remove
安装
composer require --dev dave-liddament/phpstan-rule-test-helper