こんにちは、シンクロ・フードの中川です。
今回は、弊社で運用している Rails アプリケーションにおける、CI での RSpec 並列実行のための parallel_tests 導入と、その結果について紹介します。
背景
弊社ではGitHub Actions を使って Rails アプリケーションの自動テストを実行しています。しかし、テストケースの増加に伴い、CI の実行時間が長くなり、開発サイクル全体の速度を低下させるという課題がありました。
そこで、RSpec を並列で実行できる parallel_tests という gem を導入し、CI の速度改善を行うことにしました。parallel_tests は CPU のコア数に応じて並列実行が可能であり、私たちが利用している GitHub Actions の Runner 環境 (2 コア) を最大限に活用でき、追加コスト無しで導入できるということで採用しました。
parallel_testsとは?
parallel_tests は、RSpec や Minitest などのテストを並列実行するための gem です。CPU のコア数に応じてテストを並列で実行することで、CI の実行時間を短縮できます。 github.com
導入方法
parallel_testsのインストール
gem 'parallel_tests', group: [:development, :test]
database.ymlの設定
parallel_tests の README に記載されている通り、環境変数 TEST_ENV_NUMBER をデータベース名の後に追加します。並列実行数に応じて、yourproject_test、yourproject_test2、yourproject_test3 のようにデータベース名を指定します。
database: yourproject_test<%= ENV['TEST_ENV_NUMBER'] %>
データベースの準備
- データベースの作成
parallel_tests では、並列実行する数だけ データベース を用意する必要があります。
通常は parallel:setup を使えば データベース の作成とスキーマのロードができます。
しかし、弊社では Ridgepole という gem をラップした独自の仕組みで データベーススキーマを管理しているため、parallel:setup が利用できませんでした。そこで、まず1つのデータベースに対して Ridgepole を用いたスキーマ適用を行い、その後 parallel:prepare を用いてコピーする方式を採用しました。 - MySQL の権限設定
もともと、RSpec を実行する Rails アプリケーションで使用する MySQL ユーザーに、データベースの準備のために必要なCREATE や DROP などの権限は付与していませんでした。そのため、parallel_tests を使用する開発環境と CI 環境に限定して、権限を追加しました。 - 初期データの追加
parallel_testsでは -e オプションを使うことで、複数用意されたDBに対して任意のタスクを実行できます。
弊社では、初期データの投入に seed_fu を使用しているため、以下のコマンドで実行しました。
RAILS_ENV=test parallel_test -e "rake db:seed_fu"
- データベースの作成
RSpecの並列実行
parallel_rspecコマンドでrspecを並列で実行します。
bundle exec parallel_rspec --test-options '-fd --force-color'
結果
CI実行時間の変化
アプリケーション | 項目 | Before | After | 改善率(%) |
---|---|---|---|---|
A | DBのセットアップ | 35s | 83s | -137% |
rspec | 9m51s | 6m37s | 33% | |
B | DBのセットアップ | 31s | 57s | -84% |
rspec | 3m39s | 2m32s | 31% | |
C | DBのセットアップ | 43s | 109s | -153% |
rspec | 24m58s | 20m16s | 19% |
2 並列で実行した結果、RSpec のテスト時間を 20%~30% 程度短縮できました。
一方で、データベースのセットアップ部分ではアプリケーションによっては2倍以上遅くなってしまいました。
課題
テストケースの偏りの解消
今回の検証では、アプリケーション C において、並列実行した RSpec の実行時間に差が見られました。これは、テストケースの実行時間の偏りが原因と考えられます。- 1 回目の計測: 17m4s と 19m27s
- 2 回目の計測: 17m36s と 20m36s
parallel_tests には、テストの実行時間を均等にする機能があり、それを利用することで、さらなる速度改善が期待できます。今回はログ取得の必要があったためこの機能は使用しませんでしたが、今後の課題として検討していきます。
データベースのセットアップの改善
現在の方法では、データベースを直列で準備していることと、parallel:prepare がスキーマファイルをダンプ・ロードするため、処理に時間がかかっています。RSpec の実行時間と比較すると影響は小さいものの、改善の余地があるため、より効率的な方法を検討する必要があります。
おわりに
parallel_tests 導入により、追加コストをかけずに CI 実行時間を短縮し、開発効率向上に貢献できました。
しかし、改善の余地はまだあります。今後も、CI 環境を最適化し、より迅速な開発サイクルを実現していきたいと思います。