はじめまして。4月より株式会社トラーナに入社した、 バックエンドチームのクラシマです。(@watarukura)
deploy周りの改善が好きなので、バックエンドのテスト実行時間を短縮した話をします。 テストケースを分割して、parallelで実行するようにしました。
バックエンドのテストの状況
↑こちらのスライドから更に半年、テストは1300件に近づき、アサーションも5900件近くになりました。
バックエンドは機能追加したらテストを書くルールになっていて大変治安が良いのですが、副作用としてテストの実行に30分くらいかかっていました。 テストが終わらないとプルリクエストをマージできないので、レビュー依頼する前に30分待って、指摘を受けて修正して30分待って・・・、と開発サイクルが滞ってしまいます。 コレはいかん、ということで高速化することにしました。
GitHub Actionsのmatrixを使う
paratestも試してみたのですが、うまく動作せず断念しました。 docker-composeでテスト用DBも起動してテストしているのですが、一部のテストに順序依存が発生しているようです。 スパッと諦めて、GitHub Actionsのmatrix実行を試してみます。
test: runs-on: ubuntu-latest strategy: fail-fast: false matrix: parallelism: [5] id: [0,1,2,3,4] ... - name: Run tests run: | set -xv test_target=$(find tests/Unit/ -name '*Test.php' \ | LANG=C sort \ | awk "NR % ${{ matrix.parallelism }} == ${{ matrix.id }}" \ | sed -e 's/^/<file>/' -e 's;$;</file>;' \ | tr -d '\n') sed -i '/\/tests\/Unit/c\'$test_target'' phpunit.xml ./bin/phpunit -d memory_limit=-1 --stop-on-failure --debug
テスト対象ファイルのリストを5分割して、<file>
と</file>
で囲み、普段使っているphpunit.xmlを直接書き換えています。
↓こちらの記事を参考にさせていただきました。ありがとうございます!
qiita.com
jobs.<job_id>.strategy.matrix の公式ドキュメントはこちら。 docs.github.com
matrix実行については、公式ドキュメントのサンプルにある通りNode.jsやOSなどで複数バージョンの並行テストに使うのは知っていたのですが、awk "NR % ${{ matrix.parallelism }} == ${{ matrix.id }}"
で実行対象の振り分けに使う、というのは盲点でした。
parallelismの値をもっと大きくすればよいのでは?と疑問の方もいらっしゃると思いますが、何度か試したところ5並列でも1〜2ジョブが起動待ちになってしまうことがあり、並列実行数には上限があるようです。 jobs.<job_id>.strategy.max-parallel には↓の記載があります。
デフォルトでは、GitHubはGitHubがホストしている仮想マシン上で利用できるrunnerに応じてできるかぎりの数のジョブを並列に実行します。
before: 1300件のテストを直列で実行する
after: 1300件のテストを260件ずつ並行で実行する
Yeah!
1ファイルごとのテスト数に偏りがありますが、最長でも実行時間がおおよそ1/3になりました!
苦労話
phpunit 'filename1' 'filename2'
で動くのでは? -> 最初の1ファイルしか実行されないphpunit --filter 'filename1|filename2|...'
で動くのでは? -> 何度か動かしたものの、no test found
で落ちる- ローカル環境ではdocker内でphpunitを実行するためにラッパースクリプトを使用しており、phpunitへ引き渡す引数について中身が問題なのか形式が問題なのかの切り分けが大変でした・・・
最後に
今後もdeploy頻度を増やす活動を続けていきます。 開発体験の改善が大好きなエンジニアの皆さん、ぜひ一緒に働きましょう!