大维-利达门/phpstan 规则测试助手

一个库,可以帮助更轻松地测试 PHPStan 规则

0.3.0 2023-12-14 11:23 UTC

This package is auto-updated.

Last update: 2024-09-14 18:29:39 UTC


README

此库为 PHPStan 的自定义规则测试框架提供了一些改进。

该库提供了 AbstractRuleTestCase,它扩展了 PHPStan 的 RuleTestCase

它提供了一种更简单的方式来编写自定义规则的测试。具体来说

  1. 无需在测试代码中指定行号。
  2. 只需指定一次预期的错误信息。

改进 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