はじめに
PHPUnitにはコードカバレッジ解析の機能があります。
このカバレッジの出力には、かなり時間がかかります。
チームメンバーからCIのテストが遅いので何とかしてほしいと、issuesがあがってきたので、スピード改善できないか検討し、最終的には並列実行することで、大幅に改善することができましたので、紹介します。
コードカバレッジの出力
カバレッジを出力するには、phpunitコマンド実行時のオプションに--coverage-*
を指定します。
phpunitのヘルプに詳しい説明があります。
$ bin/phpunit --help
PHPUnit 4.8.6 by Sebastian Bergmann and contributors.
Usage: phpunit [options] UnitTest [UnitTest.php]
phpunit [options] <directory>
Code Coverage Options:
--coverage-clover <file> Generate code coverage report in Clover XML format.
--coverage-crap4j <file> Generate code coverage report in Crap4J XML format.
--coverage-html <dir> Generate code coverage report in HTML format.
--coverage-php <file> Export PHP_CodeCoverage object to file.
--coverage-text=<file> Generate code coverage report in text format.
Default: Standard output.
--coverage-xml <dir> Generate code coverage report in PHPUnit XML format.
phpunitを分割して実行し、最後にカバレッジファイルをマージする
phpunitを並列で実行させるためには、複数に分割して実行する必要があります。
サブディレクトリなど、何らかの単位で分割して実行するようにし、最後に出力されたそれぞれのカバレッジファイルをマージします。
phpcovのインストール
カバレッジファイルのマージには、phpcovが必要になりますので、composerからインストールします。
$ composer require --dev 'phpunit/phpcov'
分割して実行
下記のようにディレクトリ別にテストを実行し、最後にカバレッジファイルをマージします。
bin/phpunit --coverage-php var/report/coverage_foo.cov tests/Foo
bin/phpunit --coverage-php var/report/coverage_bar.cov tests/Bar
bin/phpunit --coverage-php var/report/coverage_baz.cov tests/Baz
bin/phpcov merge --clover var/report/coverage.xml var/report/
並列でテストを実行
phpcovのREADMEに、GNU parallelで並列実行するサンプルが記載されているので、試してみます。
GNU parallelのインストール
事前準備として、GNU parallelをパッケージ経由もしくはソースをビルドしてインストールします。
-
パッケージからインストール
$ apt-get install parallel
-
ソースからインストール
$ wget http://ftp.gnu.org/gnu/parallel/parallel-20160722.tar.bz2 $ tar -jxvf parallel-20160722.tar.bz2 $ cd parallel-20160722 $ ./configure $ make && sudo make install
並列で実行
READMEに従い、並列で実行するスクリプトを準備します。
parallel --gnu --halt-on-error=now,fail=1 ::: \
bin/phpunit --coverage-php var/report/coverage_foo.cov tests/Foo \
bin/phpunit --coverage-php var/report/coverage_bar.cov tests/Bar \
bin/phpunit --coverage-php var/report/coverage_baz.cov tests/Baz
bin/phpcov merge --clover var/report/coverage.xml var/report/
READMEには、--halt-on-error=now,fail=1
のオプションは記述ありませんが、エラーが起きたときは即時にfailしてほしかったので、追加しました。
詳しくは公式のマニュアルを参照ください。
ローカルの仮想環境で確認するときの注意事項
あとは、準備したスクリプトを実行するだけなのですが、ローカルの仮想環境で実行するときには、CPU数をデフォルトの1から2以上に変更しないと、並列実行の効果がほとんど得られません。
Docker for Macの場合(2018-09-20更新)
Dockerの場合は、Preferences
からCPU数を変更できます。
参考URL
Vagrantの場合
Vagrantの場合は、Vagrantfileに下記の記述を追加します。
# Vagrantfile
config.vm.provider :virtualbox do |vb|
vb.customize ['modifyvm', :id, '--cpus', '2', '--ioapic', 'on']
end
参考URL
結果
どのくらい早くなるかは、一概には言えませんが、例えば下記のように実行時間がかかっているとすれば、7分かかっていた処理が、tests/Baz => 3分
+ phpcov merge => 1分
で4分にはなるイメージです。
- tests/Foo => 1分
- tests/Bar => 2分
- tests/Baz => 3分
- phpcov merge => 1分
上記以外のソリューション
いずれも今回は検討のみで試したわけではないのですが、上記以外にもいくつかソリューションがあります。
特定のブランチのみカバレッジを測る
polidogさんのブログ記事『CircleCIでmasterブランチのみカバレッジを測るようにphpunitを実行する』に詳しい説明があります。
CIの並列実行機能を使う
CIによっては、CI側で並列実行の機能を提供しています。
おわりに
テストの実行時間は開発効率に直接影響します。
今回紹介した方法は、特定のCIに依存したものではないので、参考にしてみてください。