【PHPStan】カスタムルールのユニットテストを作成する

# 初めに
PHPStanでは独自のカスタムルールを作成できるということを[こちら](https://qiita.com/nnhkrnk/items/936dd295132065e04999)の記事で解説しました。今回は作成したカスタムルールのユニットテストを作成してみようと思います。

# （前提）PHPStanはPHPUnitを用いてUnitTestを書くことができる
PHPStanではユニットテストにPHPUnitが採用されています。各ルールは`RuleTestCase`というクラスを継承してテストケースが作成されており、カスタムクラスでも同様にこのクラスを継承して作成します。

以下は`RuleTestCase`の一部抜粋です。（全部で見たい方は[こちら](https://github.com/phpstan/phpstan-src/blob/2.0.x/src/Testing/RuleTestCase.php)からどうぞ）

```php: RuleTestCaseクラス
/**
 * @api
 * @template TRule of Rule
 */
abstract class RuleTestCase extends PHPStanTestCase
{
	/**
	 * @return TRule
	 */
	abstract protected function getRule(): Rule;
 
    /**
	 * @param string[] $files
	 * @param list<array{0: string, 1: int, 2?: string|null}> $expectedErrors
	 */
	public function analyse(array $files, array $expectedErrors): void
	{
		// テストケースを実行する処理が書いてある
        // 見づらくなるため割愛
	}
}
```

**ポイント**
* getRule()メソッド
  * テストしたいルールクラス指定する
* analyse()メソッド
  * 実際に検証を行うメソッド
  * 引数で以下の情報をもらい、getRule()で指定したルールを検証する
    * 検証対象のファイルのパス
    * 期待値（出力されてほしいエラー）

# UnitTest作成方法
ということで、実際に実装してみます。
今回は以下のようなカスタムルールを用意しました。このテストケースを作成してみます。

```php: MethodNameRule
class MethodNameRule implements Rule
{
    public function getNodeType(): string
    {
        return Node\Stmt\ClassMethod::class;
    }

    /**
     * @param Node\Stmt\ClassMethod $node
     * @param Scope $scope
     * @return string[]
     */
    public function processNode(Node $node, Scope $scope): array
    {
        $methodName = $node->name->toString();
        // 'get' または 'post' で始まっているかをチェック
        if (!preg_match('/^(get|post)/', $methodName)) {
            return [sprintf(
                'メソッドの名前はgetかpostで始めてください！)];
        }
        return [];
    }
}

```

実際に作成するのは以下の二種類です。
* RuleTestCaseを継承した、テストケースを記載するファイル
* 検証用のPHPファイル

まずは検証用のPHPファイルを用意しました。

```php: TestObject
<?php
namespace Path\to\Project\test\data;

class TestObject
{
    /**
     * get始まりなのでOK
     */
    public function getTest() {}
    
    /**
     * post始まりなのでOK
     */
    public function postTest(){}
    
    /**
     * 始まりがgetでもpostでもないので始まりなのでNG
     */
    public function fetchTest(){}
}
```

続いて、RuleTestCaseを継承してUnitTestを作成します。

```php
<?php
namespace Path\to\Project\test\;

use PHPStan\Testing\RuleTestCase;
use Path\to\Project\rule\MethodNameRule;

class MethodNameRuleTest extends RuleTestCase
{
    protected function getRule(): \PHPStan\Rules\Rule
    {
        // ポイント1: 今回テストしたいルールを指定
        return new MethodNameRule();
    }

    /**
     * ポイント2: PHPUnitのルールでテストケースを記載
     */
    public function test_メソッド名はpostかgetから始まってないといけない(): void
    {
        // ポイント3: analyseメソッドを用いてテストを定義
        $this->analyse(
            [__DIR__ . '/data/Hoge.php'],
            [
                ['メソッドの名前はgetかpostで始めてください！', 19] // 期待値と行数
            ]
        );
    }
}
```

実行はPHPUnitのコマンドでOKです。
無事に実行できました！

```sh
> vendor/bin/phpunit .\src\test
PHPUnit 11.5.2 by Sebastian Bergmann and contributors.
Runtime:       PHP 8.2.17
.                                                                   1 / 1 (100%)
Time: 00:00.588, Memory: 36.00 MB
OK (1 test, 1 assertion)
```


# 終わりに
今回はPHPStanのカスタムルールのテストケースを作成しました。
サクッとかけて簡単なので、書いてみてはいかがでしょうか。

ここまでご覧いただきありがとうございました！