テストの分割実行というのは昔からあるテーマで、国内だと cookpad さんの rrrspec は長期間安定して運用し成功している事例として知られています。
ただこれらのプロダクトは導入も運用もめんどくさく、もうちょっと簡単にテストを分割実行したいと僕は前から考えていました。
そこで AWS CodeBuild について以前から注目して導入の検証なども行ないましたが、例えば自分が退職したとかいきなり爆死したとかなった時に今いる会社の残ったメンバーでそれをメンテし続けることは、いやまあ不可能ではないんでしょうけど負担が大きいと言わざるを得ないと判断しました。
Google CodeBuild は AWS CodeBuild よりも実用性が高そうと思ってやっていけるか考えていますが結論がでるのにあと 600 時間ぐらいはかかりそうです。
こうなると TravisCI などを使いつつそこそこの並列度でテストを実行する必要があります。そこで僕はユビレジという会社で以下のような革新的な技法を導入しました。実際に運用されている設定、コードの断片をご覧ください。
Rakefile には以下のように書いています。
(1..3).each do |i|
task :"features_#{i}" do
Dir.glob("#{Rails.root}/features/**/*.feature").sort_by{|x| File::Stat.new(x).size }.reject.with_index{|x,i2| i2 % 3 == i-1 }.each(&FileUtils.method(:rm))
sh "./bin/rake parallel:features:with_retry"
end
end
(1..3).each do |i|
task :"test_#{i}" do
Dir.glob("#{Rails.root}/test/**/*_test.rb").reject{|x| x =~ /test\/support/ }.reject.with_index{|x,i2| i2 % 3 == i - 1}.each(&FileUtils.method(:rm))
sh "./bin/rake parallel:test"
end
end
.travis.yml のほうは以下のようになっています
jobs:
include:
- stage: setup
install: skip
env:
- PARALLEL_TEST_PROCESSORS=4
script:
- make setup
- stage: test
env:
- PARALLEL_TEST_PROCESSORS=4
- TEST_SUITE="parallel:test_1"
script:
- make test
- stage: test
env:
- PARALLEL_TEST_PROCESSORS=4
- TEST_SUITE="parallel:test_2"
script:
- make test
- stage: test
env:
- PARALLEL_TEST_PROCESSORS=4
- TEST_SUITE="parallel:test_3"
script:
- make test
- stage: test
env:
- PARALLEL_TEST_PROCESSORS=3
- TEST_SUITE="parallel:features_1"
script:
- make test
- stage: test
env:
- PARALLEL_TEST_PROCESSORS=3
- TEST_SUITE="parallel:features_2"
script:
- make test
- stage: test
env:
- PARALLEL_TEST_PROCESSORS=3
- TEST_SUITE="parallel:features_3"
script:
- make test
もうこれ出オチに近いんですが要点を一つ解説しておきます。このような方法でテスト実行の並列度を上げると当然依存ライブラリとかデータベースとか JavaScript のコンパイルとかのセットアップフェーズの時間が無駄になってきます。
これをどうにかする必要があるのですが、 TravisCI には Build Stages という機能があり、ここにいろいろと押し込んでおくと楽です。
ここでは assets:precompile の結果と gem install の結果と ./bin/rails db:setup の結果を mysqldump したものを tar にまとめて s3 に渡すという形で setup フェーズと test フェーズの間で成果物を受け渡すようにしています。
CircleCI の pipeline とかだとこの辺の仕組みもうちょっとマシだったりするんでしょうか。 Docker イメージ経由でやり取りする感じ?このへんはよく知りません。
誰でも一目みれば分かる単純な仕組みでそこそこにテストを分散して実行できるのでオススメです。