Help us understand the problem. What is going on with this article?

PHPUnitを並列実行してできたJUnit XMLファイルとカバレッジファイルをマージする方法

グレンジでサーバー・インフラを担当している村田です。

グレンジ Advent Calendar 2019の1日目の記事です。

グレンジでPHPUnitを使ってテストを行っていますが、
テストが増えるにつれてテストに時間がかかることが問題になりました。

テストをテストファイル単位やディレクトリ単位などで分け、PHPUnitを並列実行すればそれだけ早く終わらせることができます。
しかし、その結果ファイルのマージはPHPUnit単体では不可能です。

テスト結果のマージする方法をまとめた記事になります。

前提条件

JUnit XMLファイルとカバレッジファイルを.build/junitと.build/phpcovに出力していることを前提としています。

% cd /home/username/src/tests/
% phpunit a_test.php                                     \
   --log-junit=/home/username/src/.build/junit/a.xml     \
   --coverage-php=/home/username/src/.build/phpcov/a.cov
% phpunit b_test.php                                     \
   --log-junit=/home/username/src/.build/junit/b.xml     \
   --coverage-php=/home/username/src/.build/phpcov/b.cov
% tree /home/username/src/.build
/home/username/src/.build
├── junit
│   ├── a.xml
│   └── b.xml
└── phpcov
    ├── a.cov
    └── b.cov

JUnit XMLファイルのマージ

PHPUnit作者のSebastian Bergmannさんがmerge-phpunit-xml.phpという、JUnit XMLファイルをマージするスクリプトを公開しています。
https://gist.github.com/sebastianbergmann/4405658

しかし、このコードは

  • マージするとテスト結果が重複される
  • ライブラリをrequireしていないのでこのままでは実行できない

という優しくない状態です。
なので、結果を重複しないように修正して、ライブラリはcomposerで取得できるように手を加えたGitHubリポジトリがこちら。
https://github.com/muratahiroshi/merge-phpunit-xml

そのDockerイメージも作成。
https://hub.docker.com/r/hiroshimurata/merge-phpunit-xml

次のように実行すると.build/junitのJUnit XMLファイルたちがマージされて、junit_merge.xmlが出来上がります。

% docker run -v /home/username/src:/data \
   hiroshimurata/merge-phpunit-xml       \
   /data/.build/junit                    \
   /data/junit_merge.xml

カバレッジファイルをマージしてCloverとHTMLで書き出す

PHPのカバレッジファイルはphpcovでマージできます。これもSebastian氏作。
https://github.com/sebastianbergmann/phpcov

Pharでも配布されていますが、GCPのCloud Buildでさくっと使えるようにこれもDockerイメージ作成。
https://hub.docker.com/r/hiroshimurata/phpcov

ただし、Dockerで実行するときは要注意。
phpcovのマージ処理はソースコードを参照するので、Dockerから参照できるようマウントしてあげます。
また、Dockerにマウントするソースコードのパスは、PHPUnit実行時と同じパスにしないといけません。

前提条件のパスに合わせて、Dockerのphpcovを実行する例。Clover形式のXMLやHTMLで書き出せたりします。

% docker run -v /home/username/src:/home/username/src hiroshimurata/phpcov:6.0.0 \
   merge /home/username/src/.build/phpcov/                                       \
   --clover /home/username/src/.build/clover.xml                                 \
   --html /home/username/src/.build/html

できたindex.htmlに何一つ結果が表示されていないようなら、ソースコードの参照、パスの位置関係に失敗しています。
covファイルの中身を見るとパスを確認できるので、それに合わせると良いです。

<?php
$coverage = new SebastianBergmann\CodeCoverage\CodeCoverage;
$coverage->setData(array (
  '/home/username/src/app/library/A.php' => # このパス
  array (

Cloud BuildでPHPUnitの実行からphpcovまでまとめて行うなら、パスの問題は生じないと思います。

まとめ

一時期、2時間近くかかっていた全テストも並列分散させて数十分に抑えることができました。
CIは手早く回せるだけ快適&緊急対応時の変な汗も減らせて快適。

おまけ boxで作るPhar

Dockerのmerge-phpunit-xmlはboxを使ってPharを作成しています。

FROM composer:1.8.5
WORKDIR /tmp/merge-phpunit-xml
ADD merge-phpunit-xml.php composer.json composer.lock box.json /tmp/merge-phpunit-xml/
RUN /usr/bin/composer install --no-dev
ADD https://github.com/humbug/box/releases/download/3.7.0/box.phar /usr/bin/box
RUN php /usr/bin/box compile

FROM php:7.2.15
COPY --from=0 /tmp/merge-phpunit-xml/merge-phpunit-xml.phar /usr/bin/merge-phpunit-xml
ENTRYPOINT ["/usr/bin/merge-phpunit-xml"]

box.jsonを書いてコンパイルすれば、1ファイルにまとめられるのでスッキリ。

box.json
{
  "alias": "merge-phpunit-xml.phar",
  "files": [
    "merge-phpunit-xml.php",
    "vendor/autoload.php"
  ],
  "main": "merge-phpunit-xml.php",
  "output": "merge-phpunit-xml.phar",
  "compression": "GZ"
}

とても重要なおまけ告知

本の執筆に参加しました。
ゲーム開発が変わる!GCPゲームインフラ実践ガイド (NextPublishing)

pakiln
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした