Slide 1

Slide 1 text

STORES 株式会社 @shia 差分ベースで 効率的にテストを実行してみる

Slide 2

Slide 2 text

SIM SANGYONG (@shia) - STORES株式会社 CTO室 - GitHub: @riseshia - Twitter: @riseshia 自己紹介

Slide 3

Slide 3 text

本題に入る前に - 対象者 関わっているプロジェクトが - テスト数が多く、全体を実行するのに時間がかかる - CI のテストの実行時間が長く開発体験に悪影響を与えてる - 主要な仕様の変更にテストの変更を伴う必要があるくらいにはテスト が充実してる

Slide 4

Slide 4 text

本題に入る前に - 話すこと - 変更の影響を受けるテストのみを選択して実行する手法の紹介 - テストセレクションの実験とその結果の紹介 - 考えられる利用ケースの考察

Slide 5

Slide 5 text

モチベーション - 長生きしているサービスのテストは多い・遅い - 仕様の複雑度とテストの数・実行時間は比例する - テストが遅いと実行完了待ちで集中力が分散しがち - サービスがでかくなっても開発の速度を維持したい - なんだったら高速にしたい => テスト実行時間を短くしたい

Slide 6

Slide 6 text

テスト実行時間を短くするのは難しい 難しい -> 費用対効果の観点で割に合わない - テスト実行を早くする - 不要なテストを消す - 並列で実行する <- よく使われる - 本当に必要なテストだけを実行する <- 今回の話

Slide 7

Slide 7 text

order_spec.rb - テストとテスト実行に使用されるロジックの依存を計算 (事前処理) - 差分からの実行すべきテストを算出する (毎テスト実行) 本当に必要なテストだけを実行する Commit A Commit B book.rb order.rb book_spec.rb order_spec.rb テストとテスト実行に使用される ロジックの依存情報 order.rb +++--- テスト選択機 book.rb order.rb book_spec.rb order_spec.rb

Slide 8

Slide 8 text

テストが依存しているロジックをどういう解像度で表すか - method based - 1 test associated to n methods - 粒度が小さい代わりにテスト選択が複雑 - コアロジックに絡む場合の効率がいい - file based <- 今回の話 - 1 test file to n source files - 粒度が大きい代わりにテスト選択が簡単 - コアロジックに絡む場合の効率が悪い 依存情報の解像度

Slide 9

Slide 9 text

手法候補 - 静的分析ベース - Ruby だとヒューリスティックがたくさんになりメンテコストが現実味 がない。 Ruby 3.0 で入った型に期待。 - 動的分析ベース <- 今回の話 - 速度面で不安があるけどとにかく楽 - 機械学習ベース - モデルの精度を実用段階まであげるところから始めないといけないので 検証の道のりが長い

Slide 10

Slide 10 text

動的分析をする 実は事例がある - https://shopify.engineering/spark-joy-by-running-fewer-te sts - メソッドコールを記録して依存グラフを作ってそれと変更差分をみてテ ストを選択したら効率良くなりました - いくつか不明な点があった - 依存情報の解像度がわからなかった - Shopify よりは小さいが並列テストが必要なくらいには大きいシステム では有効か?

Slide 11

Slide 11 text

- rotoscope - Shopify で使ってるらしい。採択 - calleree - ko1 さんが作ったもの - 呼び出し先のローケーションが取れて嬉しい - 実験時点では今回のテスト環境である Ruby 2.7 ではバグがあったため 未採択 - coverage - 実行したコードを記録するためのライブラリ - erb などのテンプレートのロギングができない - 実験終わってから気づいてしまったので未採択(速度計測はあり) 依存情報ロギングに使えるツール

Slide 12

Slide 12 text

実験 - 知りたいこと - 事前に計算しておくコストの重さ - 10倍遅くなるとかなら話にならない - 実際どれくらいテストを絞れますか - 絞れたとして選択した数が平均で全体数の 80% ならあまり嬉しくない - 絞れないケースが多くても微妙 - 正しいテストが選ばれてますか - 選ばれるべきだが選ばれてないと困る - 正しいか人の確認が必要

Slide 13

Slide 13 text

- 普通の Rails アプリケーション - テストの数は 10k+ で全体実行だと手元(MBP 14' 2021)で 2hr+ - file based 依存情報を利用 実験 - 対象プロジェクト

Slide 14

Slide 14 text

- 1. 計算した依存情報から PR ごとに選択されたテストの数 - 障害を起こしていない連続している PR 30件 - base branch … pr branch の差分を利用 - 2. 依存情報を作るのにかかる時間測定 - 有効にしている時とそうじゃない時の比較 - 3. 選択されるべきテストの選択漏れはないか 実験用で作った gem: https://github.com/riseshia/affected_tests 最小 poc: https://github.com/riseshia/test-filtering-poc 実験 - 計測対象

Slide 15

Slide 15 text

実験 - 実験結果1(テスト選択効率) - 全件実行されるものが 50% - 5%以下を選択するものが 40% - 40%以上を選択するものが 10%

Slide 16

Slide 16 text

実験 - 実験結果1(テスト選択効率) - 全件実行になったもの - テストなしのワンショット用コード / アセット / 設定の変更 - bundle update PR(ボットからの定期実行) - 40% 以上選択したもの - ビジネスロジックのコアにあるものが変更されている Shopify で出している結果とあまり変わらない。つまりこのサイズでも アプローチそのものは有効っと考えられる 

Slide 17

Slide 17 text

全体テストの一部だけを数回実行して平均をだし比較 - ロギングなし -> 8min - rotoscope -> 10.5min (31%増加) - coverage -> 8.5min (6%増加) 実験 - 実験結果2(依存情報ロギングによる速度低下)

Slide 18

Slide 18 text

知りたいこと、わかった? - 事前に計算しておくコストの重さ - 実行速度面では大丈夫そう - インフラコスト面では 1 依存情報生成に対して 2-3回実行テスト数を絞 れたらが取れそう - 実際どれくらいテストを絞れますか - 開発体験という意味だともう少し効率あげたいかも - 正しいテストが選ばれてますか - 少なくとも実験上で選ばれた差分では問題のある事例は見つからず - 動的分析でも安全!という保証はできない

Slide 19

Slide 19 text

動的分析の感想 - 実用上呼び出し元側のローケーションだけを収集してもほぼ問題ない - ほとんどの最終的な辿り着き先は別の gem/標準 lib メソッド - rotoscope はデフォルトでは呼び出したメソッドは教えてくれが、その ローケーション情報を提供しない - 検知できないものは確かにあるが、実用上でクリティカルとは思えない - e.g. モジュール内部の定数参照 - e.g. ActiveRecord モデルの属性参照 - 頑張れば全体選択の頻度は下げられそう - アセットへの参照...

Slide 20

Slide 20 text

2. 全件実行する 最新のコミットへの依存情報を計算してる間はどうするか決める必要があ る 利用体験への感想 Commit A Commit B Commit C 依存情報持ち 計算中... 1. Commit A との差分で テストを選択するしかない

Slide 21

Slide 21 text

利用体験への感想 - テスト環境セットアップにかかる速度の重要性があがる - e.g. セットアップに3分かけてテストが 10秒で終わってしまうと結局体 感として遅いまま - CI が並列数を実行中に変更可能じゃなければテスト数によって並列 数を変更できないのでやや非効率 - e.g. 選択したテストファイル数がノード数より少ないとなんもしない ノードが発生する

Slide 22

Slide 22 text

1. ベースブランチの CI/CD および PR 上のテストで運用する - デプロイまでの時間は短縮できる - テストセレクションが間違っていると障害に繋がる可能性が上がるの でリスクマネージメントが大事になる 使い道への考察

Slide 23

Slide 23 text

使い道への考察 2. PR のテスト実行時にだけ運用する - 開発中の間のテストの実行時間が短くなる - マージからデプロイまでの時間が短縮される可能性あり - ベースブランチでは全テストが動くのでデプロイのリスクは使わない 場合と同じ

Slide 24

Slide 24 text

使い道への考察 3. 手元で PR を提出する前に利用する - 変更の影響範囲を調べるために使える - 選択されたテスト数が少ない - PR で意図してないところへの影響によるテスト失敗が見つかったが CI 待ちで開発速度が落ちるのはよくあるケース - 手元で完結できて便利 - 選択されるテスト数が多い - CI 上の並列実行に任せる - プロジェクトによって手軽に実行できるテスト数は違うので価値があ るかは試してみるしかない

Slide 25

Slide 25 text

- 差分によるテスト選択手法の紹介 - 実際使うとしたらどういう使い方ができるのか ありがとうございました 終わり