$30 off During Our Annual Pro Sale. View Details »

Talk about CI and testing of the STORES

hogelog
November 08, 2023

Talk about CI and testing of the STORES

STORES Tech Talk - STORESのRailsを語る会の登壇「STORES ネットショップのCIとかテストについて話します」資料です。

hogelog

November 08, 2023
Tweet

More Decks by hogelog

Other Decks in Programming

Transcript

  1. STORES 株式会社
    STORES ネットショップのCIとかテストに
    ついて話します
    2023-11-08 @hogelog
    CTO室技術基盤グループマネージャ 小室 直
    2023年11月

    View Slide

  2. 自己紹介
    技術基盤グループ
    マネージャ @hogelog
    ウェブ企業でサービス開発エンジニア、基盤領域エンジニアやマ
    ネージャーを経由して、現在はまたエンジニアマネージャに。
    サーバサイドとインフラの間や組織と組織の間に落ちているボー
    ルを拾うのが割と好きです。

    View Slide

  3. CIとは
    3
    - CI = Continuous Integration(継続的インテグレーション)
    - CIでは色々なものを動かします
    - テスト
    - Lint
    - ……

    View Slide

  4. STORES ネットショップのテスト
    4
    - STORES ネットショップのRailsアプリケーションのテストに関し
    てあれこれ雑多に紹介します

    View Slide

  5. STORES ネットショップのテスト行数推移
    5
    - 2023年11月現在: 22万6185行
    - `find spec -type file -name '*.rb' | xargs wc -l`

    View Slide

  6. CI実行環境と実行時間
    6
    - CI実行環境: CircleCI
    - 10m+ もしばしば☔

    View Slide

  7. ある程度以上規模のRailsアプリケーションテストの問題
    7
    - 問題
    - 結果が不安定
    - 時間がかかる
    - お金がかかる
    - なんとかする
    - 不安定なテストの改善
    - テストの高速化
    (      )≒ 時間がかかる

    View Slide

  8. 不安定なテストの安定化

    View Slide

  9. テスト安定化: タイミング問題で落ちるテストの修正
    9
    - 複数データが同一秒に作成されると落ちるテスト
    - 複数データが同一秒に作成されないと落ちるテスト
    - 他

    View Slide

  10. テスト安定化: 不安定なテストの改善(レスポンスのブラウザキャッシュ)
    10
    - feature specでブラウザがレスポンスをキャッシュしてしまい実
    行順によっては失敗する場合があった
    - 現在はテスト実行時は `expires_in` を無効化している
    - 後日ブログで再現コードや他の解決方法について解説します
    module DisableBrowserCacheDuringTest
    def expires_in(seconds, options = {})
    if seconds == 0
    super
    end
    end
    end
    ActionController::ConditionalGet.prepend(DisableBrowserCacheDuringTest)

    View Slide

  11. テスト安定化: 不安定なテストの改善(遅い回線のエミュレート)
    11
    - CIでネットワークが詰まった時に不安定になるテストを見つけるた
    めの遅い回線のエミュレート
    if ENV["CAPYBARA_EMULATE_SLOW_NETWORK"]
    emulate_network_config = {
    latency: 100, # 100ms
    download_throughput: 64 * 1024 / 8, # 64Kbps
    upload_throughput: 64 * 1024 / 8, # 64Kbps
    }
    elsif ENV["CAPYBARA_EMULATE_NETWORK_CONFIG"]
    emulate_network_config = JSON.parse(ENV["CAPYBARA_EMULATE_NETWORK_CONFIG"], symbolize_names: true)
    end
    if emulate_network_config
    Capybara.current_session.driver.browser.devtools.network.emulate_network_conditions(offline: false,
    **emulate_network_config)
    end

    View Slide

  12. テスト安定化: 不安定なテストの改善(集計と継続的取り組み)
    12
    - 不安定なテストをスプレッドシートに集計、可視化し改善に日々取
    り組み

    View Slide

  13. テスト安定化: rspec --only-failures
    13
    - 実行に失敗したテストのみ再実行するフラグ
    - 不安定なテスト失敗時、テストを全て実行しなおす時間をスキップ
    できる

    View Slide

  14. テスト安定化: rspec-retry
    14
    - 実行に失敗したテストのみ再実行するgem
    - 同一rspec実行プロセスの中で再実行されるので--only-failuresよ
    りも速いことが期待できる

    View Slide

  15. テスト再実行系ソリューションの注意点
    15
    - 不安定なテストが気づかぬうちに増えていく
    - 不安定なテストが増えると実行時間も伸びる
    - 継続的な改善活動に取り組んでいない場合、必ず不安定なテストは
    増えていきます
    - CircleCIならばCircleCI Insightsなどを見るのもオススメ
    - そもそも再実行系ソリューションは可能な限り導入を避けたい

    View Slide

  16. テストの高速化

    View Slide

  17. テスト高速化: Capybara.default_max_wait_time の調整
    17
    - feature specの失敗時、Capybara.default_max_wait_time時間
    待つ場合があるので、長すぎない程度の適切な時間を設定
    - 現在の設定: `default_max_wait_time = 10`

    View Slide

  18. テスト高速化: 大量に作られるテストデータの削減
    18
    - 検証したい条件に関連しない無駄なテストデータ作成を削減
    - かなり地道
    - 101.times { |i| Fabricate.create(...) }
    + Fabricate.create(...)

    View Slide

  19. テスト高速化: テスト分割と並列実行(CircleCI)
    19
    - 現在の設定: `parallelism: 16`
    - 💰

    View Slide

  20. テスト高速化: テスト分割と並列実行(parallel_rspec)
    20
    - CircleCIのparallelismはマシンリソースを使いきれないので
    parallel_rspecで並列度を更に向上📈
    - 複雑度も向上📈、悩ましい

    View Slide

  21. テスト高速化: テスト分割と並列実行(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.compact
    puts "Rerun #{failing_examples.size} examples"
    command = "bundle exec rspec --no-fail-fast -- #{failing_examples.join(" ")}"
    puts command
    exec command

    View Slide

  22. テスト(CI)高速化: CircleCI workspaceの省サイズ化
    22
    - ジョブ間のデータ共有に用いるworkspaceの省サイズ化`rm -rf
    .git/ ...`

    View Slide

  23. テスト(CI)高速化: 細かい改善
    23
    - テスト失敗あるときのみ `rspec --only-failures` 実行
    - 無駄なapt-get installの削減
    - 他

    View Slide

  24. テスト(CI)高速化: 遅いテストを観測し改善する継続的な取り組み
    24
    - 基本は rspec --profile
    - 集計しavg, p95など可視化すると効果的
    - CircleCI Insightsなども有用

    View Slide

  25. テスト安定化・高速化
    25
    - しばらくテスト安定化・高速化の取り組みがない、アクティブに開
    発されているRailsアプリケーションは必ずテストが不安定になり
    遅くなっています
    - CIの結果や設定ファイルをじっと見ると改善できる場所が必ず浮か
    んでくる
    - できるまでやればできる

    View Slide

  26. 今後のテストについて
    26
    - CircleCI -> GitHub Actions, CodeBuild, Jenkins, …?
    - 不安定なテスト、遅いテストがまた蓄積しつつある
    - To be continued...

    View Slide