PHPUnitのカバレッジレポート(XML)を使ってカバレッジの計算してみた

LINEで送る
Pocket

こんにちは
仕事の方でテストカバレッジをGUIなしに集計する必要が出たので、

  • メソッド単位のカバレッジを集計したい
  • クラス単位でのカバレッジを集計したい
  • ファイル単位でのカバレッジを集計したい
  • ディレクトリ単位でのカバレッジを集計したい

の集計をするために、PHPUnitが出力するClover形式のXMLと格闘して得られた、XMLの構造と扱い方についてまとめてみました

はじめに

レガシーなPHPと戦っており、PHPUnitのバージョンは3.4です
最高につらい

なので出力されるXMLの構造や属性名に差異があるかもしれません
また、カバレッジレポートの出力方法はこちらのドキュメントを参照して下さい

なお、PHPUnitのカバレッジレポート単体ではC0のカバレッジしか計測できませんでした
後述するlineタグのnum属性とcount属性の値を使って対象プログラムの静的解析かければ、解析できなくはないかもしれませんが、
カバレッジレポート単体ではC0のレポートしか出ません。

カバレッジXMLの書式

この当時のPHPUnitには--coverage-cloverというオプションがあります
これがXML形式のカバレッジレポートを出力してくれるオプションです

カバレッジレポート(XML)の基本的な構造

XMLはざっくり、こんな感じになりました

有効行数は、空白行やコメントアウトなどを除いた、PHPのコードとして評価される行数を指しています。

クラス単位で行カバレッジを取る

jsで書くと、

に相当します

classタグ1つにつきmetricsタグが1つはいっているので、
目的のクラスの中にあるmetricsタグを抽出し、有効行数とカバーしている有効行数で比較できます。

namespaceを入れないと衝突する恐れがあります。
もしその辺考慮しなくていいならnamespace属性は無視できます。

ファイル単位で行カバレッジを取る

ファイルも同じ要領で、fileタグ1つの直下にmetricsタグが1つ入っているので、それを比較します。

ファイル名(basepath相当)ではなく、フルパスな点に注意です。
テストを実行(カバレッジ集計)した環境によって変わるのでご注意下さい。

ディレクトリ単位でカバレッジを取る

カバレッジレポートはfileタグ単位で纏まっているので、ディレクトリごとのカバレッジを完璧に取ることは困難です。
もしソースの実体があれば ディレクトリの中身を漁って有効行数を出すことが可能ですが、
ソースの実体を持たない限り、カバレッジレポートに記載されているファイルしか計測対象になりません。

その不完全な状態であれば、

相当で取得可能です。
ディレクトリ内部のファイルのメトリクスをかき集めて、最後に合算すれば算出可能です

メソッド単位でカバレッジを取る

※前置きでも話しましたが、バージョンアップによって改善されている可能性もあります。あくまで古いPHPUnitについて言及します。

メソッド単位も、完全な情報は出せません
いや、正確にはメソッドに関するレポートなら出せます。が、関数に関するレポートが出せません しかも不完全な情報の収集ですら地味に面倒でした

lineタグのtype属性はstmtmethodにしかならず、関数の定義開始行はtype=stmtになってしまいます
関数対して判別可能な値が何もありません。計測不可能です

これもソースの実体があれば静的解析と絡めてレポート可能だとは思いますが、レポート単体では計測不可能でした
なので関数のレポートは出ないという前提で良ければ、

ただし、これは不完全です
例えばクラスに属さない関数がファイルに含まれている場合に対応できません
完全なカバレッジを得るためにはソースコードと静的解析が必要です。

PHPUnitの設定ファイルにてカバレッジ集計対象の設定をして、
テストに登場しなかったファイルもカバレッジ集計対象に加えることは可能ですが、結局のところ関数には対応できません

まとめ

  • 行カバレッジの解析はXMLの構造さえ分かれば結構簡単
    • ファイル単位、クラス単位でのカバレッジなら確実に取れる
    • 関数が1つ以上定義されているファイルに対しては、ソースと突合しないとカバレッジ集計不可能
  • PHPには結構豊富なリフレクションのメソッドがあるので、突合は技術的には可能。今回は使ってない

結果的な感想としては「ツラい」のただ一言でした。

LINEで送る
Pocket