PHPUnitでカバレッジがうまく取れなくて嵌った話
はじめに
以前、こんな記事を書いていました
こちらの時とは別のプロジェクトでカバレッジがうまく取れなくて嵌ったお話をします。
やけにカバレッジ低くない?
新規プロジェクトだったということもあり、初期段階で Laravel9 から使える様になったテストカバレジオプションで雑にカバレッジを見ていました
$ php artisan test --coverage
開発サイクルも回り始めて、いよいよちゃんとカバレッジを見れるようにと上記の様にカバレッジを PR コメントで見えるようにしました。
そこで異変に気づきました。やけにカバレッジが低いぞと。。
原因で考えられたこと
新しいプロジェクトなので微妙にバージョンが違います
- PHP のバージョン違い
- PHPUnit のバージョン違い
レポートの指定も変わりました
https://phpunit.readthedocs.io/en/9.5/configuration.html#the-report-element
カバレッジ取得後に別途レポート出力用のコマンドを実行しなくても良くなりました。
phpunit.xml に設定するだけでレポート出力までやってくれて便利! - 拡張モジュールの違い
pcov から XDebug
色々変更点がおおく、調査も難航しました。
同僚がまず先行して調べてくれていましたが、お手上げで自分も色々設定まわりやらなにんやら調べて見てもわからん!となりました。。
原因だったもの
自分が原因を作り込んでいました orz
今回のプロジェクトから PHPUnit の書き方で1つ変えてた所があり、 アノテーションを入れてテストコードの追跡性を良くしましょう
というのがありました。
@covers です
これを使うとテストの目的が明示されるので良いのと、なにより IDE のサポートが得られプロダクションコードからテストコードに jump させることができます。
jump できない場合はテストコードが書いてないことがわかり、便利だなーと思いコツコツとアノテーションを埋め込んでいきました。
@covers
アノテーションの書き方はいくつかあり、以下の表がまとめられていました
@covers ClassName
@covers ::functionName
のみが推奨されているようです。
後者は @coversDefaultClass
と組み合わせて使います。
実際に上記の図のサンプルコードの様に記載していきました。
こちらのアノテーションが起因していました。
見落としていたもの
調べていて初めて気づきました orz
@covers アノテーションが利用された場合、出力されるコードカバレッジの算出方法が変わります。 アノテーションで自身を指定するテストから実行された場合のみ、「covered」と判定されます。 アノテーションで自身を指定していないテストから間接的に利用されたとしても、「covered」とは判定されません。 これによって、コードカバレッジが実際よりも高く出る false positive を避けることができます。
確かにテストコード内で直接呼び出しを行っているコード。。特に private メソッドは全てカバレッジが取得されていませんでした。
対応したこと
- テストクラスの docblok に
@coversDefaultClass
に指定したクラスを@covers
にも指定する - 上記とあわせて、テスト対象クラスが依存しているファイルもあわせて
@covers
に追加する
単体テストが書かれているものは記載せず、単体テストが難しい [1] クラス。。例えば Eloquent モデルとかを追加 - PHPUnit の設定に
forceCoversAnnotation="true"
を追加
これを入れておくとテスト実行時にアノテーションが未指定の場合に WARN となります
やらなかったこと
このアノテーションはテストクラスの docblock に記載することも、個別のテストメソッドの docblock に記載することもできますが、 テストクラスの docblock に記載することが推奨され、個別のテストメソッドの docblock に記載することは推奨されません。
@covers
を個別のテストメソッドの docblock に記載することは推奨されないっぽいですが、上述の IDE の恩恵にあやかりたく、直接テストメソッドまで jump させたかったのでこちらの推奨は守りませんでした
所管
@coversDefaultClass
をテストクラスの docblock に記載したら、その対象クラスが全て全て coverage 対象になってくれれば良いのに。。と思いつつも
コードカバレッジが実際よりも高く出る false positive を避けることができます。
こちらを優先されたんだろうなーと。
厳密にテスト対象を把握させろ(しろ)という意思を感じました
この記事で伝えたかったこと
ですハイ。
肝に命じます。。
あと @covers
の指定はこうやって書くんだよーというのがあれば知りたいです。
-
敢えて割愛しているともいう。。 ↩︎
Discussion