10
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

グレンジAdvent Calendar 2019

Day 1

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

Last updated at Posted at 2019-11-30

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

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

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

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

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

前提条件

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

.sh
% 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
.sh
% 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が出来上がります。

.sh
% 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で書き出せたりします。

.sh
% 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
<?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)

10
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?