PHPStanのテストコードを書くのに役立つRectorやPHP Parserの知識
先日PHPStanのテストコードを書く機会があり、発見があったので控えておく。
お題はTypeSpecifier
のテスト。TypeSpecifier
のテストがtests/PHPStan/Analyser/TypeSpecifierTest.php
にある。一部を抜粋する。(phpstan/phpstan-src)
/**
* @dataProvider dataCondition
* @param mixed[] $expectedPositiveResult
* @param mixed[] $expectedNegatedResult
*/
public function testCondition(
Expr $expr,
array $expectedPositiveResult,
array $expectedNegatedResult
): void {}
public function dataCondition(): iterable
{
if (PHP_VERSION_ID >= 80100) {
yield [
new Identical(
new PropertyFetch(new Variable('foo'), 'bar'),
new Expr\ClassConstFetch(new Name('Bug9499\\FooEnum'), 'A'),
),
[
'$foo->bar' => 'Bug9499\FooEnum::A',
],
[
'$foo->bar' => '~Bug9499\FooEnum::A',
],
];
これがどういうテストなのかはさておき、テストデータの1つ目の要素(new Identical
の部分)に注目。
これは解析対象のノードを生成するロジックなんだけど、見慣れてないと「??」ってなる気がする。ぱっとみRectorのNode Overviewを連想させる見た目。
テストを書こうとする時、まずはテストしたいPHPのコードが頭の中にあるはずで、それをこのようなnew ノード
の様式にマッピングする必要がある。慣れれば頭の中でできるかもしれないが、PHP Parserを使って解析すれば手っ取り早く確実に変換できる。そのやり方をここにまとめた。(--var-dump
モードで解析した方がわかりやすい。) これで、どんなノードをnewすればよいか、どんなノードがどんなノードを内包すればよいかがわかる。
ただし、PHP Parserの実行結果はnew ノード
形式ではないので、まんまコピペすることができない。ゆえに、転記し間違える可能性がある。信頼できないテストコードはむしろ害になるので正しく書きたい。なので、狙った通りのPHPコードが復元されるかどうかを確かめたい。そこでRectorのカスタムルールが利用できる。カスタムルールの書き方はこちら。
以下のようなカスタムルールと置換対象のコードを作成して、Rectorを実行するとPHPのコードが得られる。
カスタムルール
public function getNodeTypes(): array
{
// ノードはなんだってよい。便宜的にVariableにした
return [Variable::class];
}
/**
* @param Variable $node
*/
public function refactor(Node $node): ?Node
{
$variableName = $this->getName($node);
// 無限再帰しないように変換後の変数名と異なる変数名にする
if ($variableName !== 'unluckyTakuo') {
return null;
}
// テストデータの1つ目の要素をreturnする
return new Identical(
new PropertyFetch(new Variable('foo'), 'bar'),
new Expr\ClassConstFetch(new Name('Bug9499\\FooEnum'), 'A'),
);
}
置換対象のコード
<?php
$unluckyTakuo;
↓
<?php
$foo->bar === Bug9499\FooEnum::A;
置換後のコードがテストしたいPHPのコードであればOK。
以上
Discussion