STORES Tech Talk - STORESのRailsを語る会の登壇「STORES ネットショップのCIとかテストについて話します」資料です。
STORES 株式会社STORES ネットショップのCIとかテストについて話します2023-11-08 @hogelogCTO室技術基盤グループマネージャ 小室 直2023年11月
View Slide
自己紹介技術基盤グループマネージャ @hogelogウェブ企業でサービス開発エンジニア、基盤領域エンジニアやマネージャーを経由して、現在はまたエンジニアマネージャに。サーバサイドとインフラの間や組織と組織の間に落ちているボールを拾うのが割と好きです。
CIとは3- CI = Continuous Integration(継続的インテグレーション)- CIでは色々なものを動かします- テスト- Lint- ……
STORES ネットショップのテスト4- STORES ネットショップのRailsアプリケーションのテストに関してあれこれ雑多に紹介します
STORES ネットショップのテスト行数推移5- 2023年11月現在: 22万6185行- `find spec -type file -name '*.rb' | xargs wc -l`
CI実行環境と実行時間6- CI実行環境: CircleCI- 10m+ もしばしば☔
ある程度以上規模のRailsアプリケーションテストの問題7- 問題- 結果が不安定- 時間がかかる- お金がかかる- なんとかする- 不安定なテストの改善- テストの高速化( )≒ 時間がかかる
不安定なテストの安定化
テスト安定化: タイミング問題で落ちるテストの修正9- 複数データが同一秒に作成されると落ちるテスト- 複数データが同一秒に作成されないと落ちるテスト- 他
テスト安定化: 不安定なテストの改善(レスポンスのブラウザキャッシュ)10- feature specでブラウザがレスポンスをキャッシュしてしまい実行順によっては失敗する場合があった- 現在はテスト実行時は `expires_in` を無効化している- 後日ブログで再現コードや他の解決方法について解説しますmodule DisableBrowserCacheDuringTestdef expires_in(seconds, options = {})if seconds == 0superendendendActionController::ConditionalGet.prepend(DisableBrowserCacheDuringTest)
テスト安定化: 不安定なテストの改善(遅い回線のエミュレート)11- CIでネットワークが詰まった時に不安定になるテストを見つけるための遅い回線のエミュレートif ENV["CAPYBARA_EMULATE_SLOW_NETWORK"]emulate_network_config = {latency: 100, # 100msdownload_throughput: 64 * 1024 / 8, # 64Kbpsupload_throughput: 64 * 1024 / 8, # 64Kbps}elsif ENV["CAPYBARA_EMULATE_NETWORK_CONFIG"]emulate_network_config = JSON.parse(ENV["CAPYBARA_EMULATE_NETWORK_CONFIG"], symbolize_names: true)endif emulate_network_configCapybara.current_session.driver.browser.devtools.network.emulate_network_conditions(offline: false,**emulate_network_config)end
テスト安定化: 不安定なテストの改善(集計と継続的取り組み)12- 不安定なテストをスプレッドシートに集計、可視化し改善に日々取り組み
テスト安定化: rspec --only-failures13- 実行に失敗したテストのみ再実行するフラグ- 不安定なテスト失敗時、テストを全て実行しなおす時間をスキップできる
テスト安定化: rspec-retry14- 実行に失敗したテストのみ再実行するgem- 同一rspec実行プロセスの中で再実行されるので--only-failuresよりも速いことが期待できる
テスト再実行系ソリューションの注意点15- 不安定なテストが気づかぬうちに増えていく- 不安定なテストが増えると実行時間も伸びる- 継続的な改善活動に取り組んでいない場合、必ず不安定なテストは増えていきます- CircleCIならばCircleCI Insightsなどを見るのもオススメ- そもそも再実行系ソリューションは可能な限り導入を避けたい
テストの高速化
テスト高速化: Capybara.default_max_wait_time の調整17- feature specの失敗時、Capybara.default_max_wait_time時間待つ場合があるので、長すぎない程度の適切な時間を設定- 現在の設定: `default_max_wait_time = 10`
テスト高速化: 大量に作られるテストデータの削減18- 検証したい条件に関連しない無駄なテストデータ作成を削減- かなり地道- 101.times { |i| Fabricate.create(...) }+ Fabricate.create(...)
テスト高速化: テスト分割と並列実行(CircleCI)19- 現在の設定: `parallelism: 16`- 💰
テスト高速化: テスト分割と並列実行(parallel_rspec)20- CircleCIのparallelismはマシンリソースを使いきれないのでparallel_rspecで並列度を更に向上📈- 複雑度も向上📈、悩ましい
テスト高速化: テスト分割と並列実行(parallel_rspec不安定テストの再実行)21- rspec-retry + 失敗したspecの再実行- rspec-retryでは救いきれないパターンを再実行でカバー- parallel_rspec x rspec --only-failures がうまく動かないので自前スクリプトでbefore_result = File.read("tmp/test_results/failing_specs.log")failed_output = (before_result =~ /Failed examples:[\r\n]+(.+)/m && $1)failing_examples = failed_output.lines.map do |line|$1 if line =~ %r[rspec ('.+'|./spec/.+:\d+)]end.compactputs "Rerun #{failing_examples.size} examples"command = "bundle exec rspec --no-fail-fast -- #{failing_examples.join(" ")}"puts commandexec command
テスト(CI)高速化: CircleCI workspaceの省サイズ化22- ジョブ間のデータ共有に用いるworkspaceの省サイズ化`rm -rf.git/ ...`
テスト(CI)高速化: 細かい改善23- テスト失敗あるときのみ `rspec --only-failures` 実行- 無駄なapt-get installの削減- 他
テスト(CI)高速化: 遅いテストを観測し改善する継続的な取り組み24- 基本は rspec --profile- 集計しavg, p95など可視化すると効果的- CircleCI Insightsなども有用
テスト安定化・高速化25- しばらくテスト安定化・高速化の取り組みがない、アクティブに開発されているRailsアプリケーションは必ずテストが不安定になり遅くなっています- CIの結果や設定ファイルをじっと見ると改善できる場所が必ず浮かんでくる- できるまでやればできる
今後のテストについて26- CircleCI -> GitHub Actions, CodeBuild, Jenkins, …?- 不安定なテスト、遅いテストがまた蓄積しつつある- To be continued...