CIコスト下げたい、テスト実行時間減らしたい時のアイデアまとめ

友人にcircle ci 料金下げる。または、rspec実行時間減らした時にやるべきことを聞かれたのでまとめておく

Railsプロダクト前提です。

  • テストしない (野蛮だけどアイデアの一つとしてね....)
  • 不要なコードとテストを削除
  • 並列化
  • 完了時間は実行時間の長いテストに引きずられるので実行時間が均一にする(並列化した後)
  • bundle config使ってdevelopment groupライブラリをinstallさせない (メモリも少なくなります)
  • bundle install や yarn installを別jobで動かしコンテナイメージ作成。テスト実行jobでコンテナfetch
  • draft prだったらcircleci実行しない。open prだけci回す
  • skip ci 活用する
  • resource_class調整
  • 不安定なテストを直す(リトライしている場合)
  • defaultブランチ以外ではカバレッジなどのレポート系を取らない。正直テストちゃんと書くチームにしかいなかったのでカバレッジ不要だと思ってる。
  • logレベル warnとかに変更する (ログ出力量減らす)
  • gemだけではなくwebpack, asset:compile などもcacheする
  • circleci以外を利用する。ex: github action使う

「金払えば速くなるか考える」その後に「金安くなる方法考える」のが効率良いと思ってる。

一番重要なのは各stepの計測結果を地道に見て改善施策考えること。やっていこう。

追記

他にアイデアあったら追記したいので教えてね!

https://twitter.com/imaharuTech/status/1687084010651234304 にリプライよろしく

奈良在住 お酒好きからのお便り (転記承諾済)

Railsの話

  • bootsnapをテスト環境で入れておく
    • まれに古いレイルズ設定を引きずっていて開発環境だけrequireしてたり、削除していたりする
    • Railsを何度も再起動すると効いてくる

Rspec

  • できる限りlet!をやめてletにする(不要なケースでもレコード生成をしていることが多い)
    • とくにグローバル(最も外側のcontext)でlet!とかbeforeとかでいらないレコードを作ってるのを外して、必要最小限のテストケースのコンテキストだけに閉じ込める
  • factoryで「気を利かせて」関連する親・子レコードを作っているのをやめて必要な箇所で都度生成する
    • とくに関連子レコードをつくるコールバックはtraitに閉じ込める(明示的に指定しない限り暗黙生成しない)
  • system spec(シナリオテスト)は最小限にする(考え方によるけどフロント分離してたらいらない感)
    • 組合せ爆発しがちな網羅的なテストは、なるべく単体テスト(クラス1つだけの軽いテスト)に寄せる
    • requestスペックはざっくり正常系とメジャーな異常系を見るとか、認証認可だけを見るとかにする(考え方による)
  • 単体テストで1ケースに複数期待値を書くのを許す
    • 個人的には「ケースで表明していること、中でやっていることが合わなくなって改修で死ぬ」「単体テストなのに手続的なシナリオテストが頻繁に書かれる」ので、メンテ性が悪化するため反対。だけど考え方による

e2eの話

  • system spec(capibara)重い。cypressも結構重かった。PlayWrightよかった(けど、これはrailsじゃない現場だからわからない)
  • フレームワークによって結構速度違う感ある
  • あとchrome前提か、electronでやるのか、他のブラウザも担保したいのか・・(減らすに越したことないけど、オプションまずってなぜかelectronベースでやってるとかはあった)

CIの話

  • 共通の依存に関してキャッシュを利用する
    • ステップごとに全部ダウンロードしなおすとかよくあるので、gem関係はキャッシュを使う
      • ただしnode_modulesはめちゃくちゃ増えがちで、実際はキャッシュがあふれて無効になることも多いので、色々みてお手盛り調整が必要
    • keyを適切に貼る
      • バージョンが違うキャッシュがやってきたら無駄だけでなく、最悪エラーにもなるし、同バージョンの上書き合戦になってキャッシュ全体の有効性が薄まる
    • persist_to_workspaceをうまく利用する
      • セーブとかロードの時間が余計にかかるので、実際これで速くなるのか要計測感ある
  • マルチステージビルドつかう
  • 全ステップ通過を要求するかどうか吟味(とくにe2eの使い所)
    • たとえばgit flowのようなリリースフローだったとして、「ブランチpushや、本番リリースではe2e回す」「mainマージ(stgリリース)時はe2e回さない」とかrequirementを吟味する