こんにちは!
2022 年にWantedly に新卒入社し、DX Squad でエンジニアをしている大森です。
今回は、日本最大の Ruby に関するカンファレンスである RubyKaigi に Wantedly がスポンサードし、いくつかの講演を聴講しています。Wantedly はスポンサーブースを設けていて、アンケートに答えてくださった方にはRubyKaigi 2023 のために描き下ろした技術書をお渡ししており、Twitterをフォローしてくださった方には 過去のTechBookなどを配布しています。
本記事では、2日目の発表である The Resurrection of the Fast Parallel Test Runner について紹介させていただきます。
この発表では、並列テストの重要性とそれらを実現する方法、スピーカーがメンテンナンスを再開させた test-queue に関する興味深い洞察が共有されました。
The Resurrection of the Fast Parallel Test Runner
高速なテストの重要性
テストコードの重要性は、開発者の間で広く認識されています。しかし、サービスが大きくなり、その結果としてテストが増えると、新たな問題が浮上します。具体的には、全てのテストを実行するのにどれくらい時間がかかるかという問題です。スピーカーは、全テストが10分以内で終わることが良いテストだと話していました。
しかし、テストが増えるにつれて、スローテストが問題になってきます。
では、遅いテストを早くするにはどうすれば良いのでしょうか。今回は、4つ紹介されていました。
一つ目は、遅いテストをチューニングして早くする方法です。rspec -p 30 のように実行することで、遅いテストの上位30を表示し、それらをチューニングしてテスト時間を短縮することが可能です。
二つ目は、Database Cleaner の Strategy をチューニングする方法です。Strategy には Deletion や Trucation などがありますが、それぞれのパフォーマンスを計測し、最適なものを選択することが重要だそうです。
三つ目は、単純にお金を投じることにより、CPUやメモリを増強し、テストの実行を高速化する方法です。
そして最後に、直列ではなく、並列でテストを実行する方法があります。並列テストを行うことにより、テストの実行時間を大幅に短縮することが可能です。
Ruby で並列テストを実現する方法
並列テストを方法はいくつかあると思いますが、セッションでは4つ紹介されていました。
まず、Ruby のテストフレームワークである minitest を利用する方法です。 minitest では、 Rails の ActiveSupport::TestCaseを利用しています。 ActiveSupport::TestCase の中には parallelize_me!というメソッドがあり、これを呼び出すことでマルチスレッドによる並列テストが可能になります。さらに、並列化戦略はプラガブルな設計になっているため、容易に変更することができます。ただし、テストがメモリ内で完結する場合、スレッドを起動するオーバーヘッドが大きいため、テストの速度が低下することもあります。
次に、Rails 7 を利用する場合です。Rails 7でも ActiveSupport::TestCase を利用しますが、テスト数が50を超えると自動的に並列で実行されるようになります。 Rails はデフォルトで minitest を利用してテストを書くことができますが、並列化戦略は ActiveSupport::Testing::ParallelizeExecutor を用いて差し替えられています。ActiveSupport::Testing::ParallelizeExecutor はマルチスレッドではなく、マルチプロセスを使用します。ただし、 RSpec でテストを書きたいときは、上述したような並列テストはサポートされていないため、テストを並列化することはできません。
次に、parallel_tests についてです。parallel_tests は、事前にテストを分割し、parallel gemを利用して並列実行します。そのため、RSpec でも利用できます。しかしこのアプローチには欠点があります。テストを分割して実行した場合、実行時間が長い workerと短い workerが存在することになります。もちろん、直列実行したときよりは短くなります。しかし、テストの全体的な実行時間が、最も時間がかかる workerに引っ張られ、テストの分割方法を変えることで減らす余地がある時間が生じる可能性があるということです。
最後に、test-queue について解説されました。test-queue では、テストを queue に入れ、worker がそれを取り出して処理します。これにより、事前にテストを分割することのデメリットを避けることができます。しかし、test-queueは当時メンテナンスされていませんでした。しかし、設計は非常に優れていると考えたため、スピーカーがメンテナになることを決意したそうです。
test-queue の復活
スピーカーがメンテナになることで、test-queue は4,5年ぶりにメンテナンスが再開されました。
しかし、再開時には CI が壊れていたようです。そのため、開発を始める前にまずは CI の復旧が必要でした。具体的には、Travis CI から GitHub Actions への移行作業を行ったそうです。
CIを復旧させることで、ようやく開発が可能となりました。そして、開発中の RSpec 4もサポートするように改善が進められました。
スピーカーの意見としては、test-queue のような並列テストランナーは有用ですが、それよりも、RSpec自体が並列テストランナーの機能を持つ方が便利ではないかと考えているそうです。
感想
セッションでは、minitest や test-queue がどうやって並列実行をおこなうのか、といった内部実装についても触れられていました。普段利用しているときには、どうやって並列テストが実現されているか知らなかったので、勉強するきっかけになり嬉しいです。テストを書くことも大事ですが、どうやってテストが実行されているかというところにも注意してみたいと思いました。