11.1K Views
September 11, 24
スライド概要
2024/09/11 RustのWebアプリ開発 LT道場 〜テスト編〜
https://findy.connpass.com/event/327824/
Working at Deno Land, Studying at Georgia Tech
flakyテスト奮闘記 〜 Deno Deployの場合 2024/09/11 RustのWebアプリ開発 LT道場 〜テスト編〜 Yusuke Tanaka
Who am I 米ジョージア工科大学のオンライン修士 課程に通いながらDeno Land Inc.で Deno Deployを作っています🦕 ← リアルで会った方にはもれなくこの名 刺をプレゼント! GitHub: https://github.com/magurotuna 𝕏: https://x.com/yusuktan
今日の内容
Deno Deploy開発における 😈 flaky test 😈
Rustに関わる話、 Rustに限らない話、 ざっくばらんにご紹介
What is a flaky test?
What’s wrong with it?
flaky testの問題点 ● 「とりあえずrerunしとこ」のマインドになってしまう ○ reliableに失敗するテストに気づくのが遅れる ○ CIで多大な時間を要することになり、生産性が落ちる ● 往々にして単体では発生確率が低いので、再現方法や原因が分かりにく く、直すのが大変がち
What’s Deno Deploy?
Deno Deployとは? ● Node.jsの作者Ryan Dahlが反省 を踏まえて作り直したJS/TSランタ イムDeno ● それをベースにしたサーバーレス サービス ● Cloudflare Workers, AWS Lambda, Vercel のような感じ ● Netlify, SupabaseなどのEdge Functionの実行基盤でもある
Architecture?
Deno Deploy アーキテクチャ だいぶ省略してます
flaky tests breakdowns
1. 依存している外部サービスの不安定性
1. 依存している外部サービスの不安定性 ● 例: GitHub OAuthによるサインイン
1. 依存している外部サービスの不安定性 ● 例: GitHub OAuthによるサインイン ○ Deno DeployのE2Eテストのシナリオでは、GitHub OAuthによる ログイン処理が必要
1. 依存している外部サービスの不安定性 ● 例: GitHub OAuthによるサインイン ○ Deno DeployのE2Eテストのシナリオでは、GitHub OAuthによる ログイン処理が必要 ○ ヘッドレスブラウザによりユーザーの操作フローを再現
1. 依存している外部サービスの不安定性 ● 例: GitHub OAuthによるサインイン ○ Deno DeployのE2Eテストのシナリオでは、GitHub OAuthによる ログイン処理が必要 ○ ヘッドレスブラウザによりユーザーの操作フローを再現 ○ 😨 GitHub側の実装に依存してしまう ■ Passkeyを使いませんか?の画面が突然追加される ■ 入力フォームのname属性の値が変わる
1. 依存している外部サービスの不安定性 ● 例: GitHub OAuthによるサインイン ○ Deno DeployのE2Eテストのシナリオでは、GitHub OAuthによる ログイン処理が必要 ○ ヘッドレスブラウザによりユーザーの操作フローを再現 ○ 😨 GitHub側の実装に依存してしまう ■ Passkeyを使いませんか?の画面が突然追加される ■ 入力フォームのname属性の値が変わる ■ なんかたまに落ちる ← 一番厄介
1. 依存している外部サービスの不安定性 ● 例: GitHub OAuthによるサインイン ○ 😨 GitHub側の実装に依存してしまう ■ なんかたまに落ちる ← 一番厄介 ● 対策: ヘッドレスブラウザを録画してGitHub ActionsのArtifactとして保 存するようにした ○ デバッグがしやすくなった 🥳 ○ 人間がやるとワンタイムパスワード入力後自動で次の画面に遷移す るのが、ヘッドレスブラウザ上だと遷移していなかった
2. 非同期で動く複数コンポーネント間の不整合
2. 非同期で動く複数コンポーネント間の不整合 ● 例: Deno Deployでユーザーのコードが console.log などで吐いた ログが保存されていることを確認するテスト
2. 非同期で動く複数コンポーネント間の不整合 ● 例: Deno Deployでユーザーのコードが console.log などで吐いた ログが保存されていることを確認するテスト ○ 複数のレイヤーでバッファリングされる (runner, origin, etc.)
2. 非同期で動く複数コンポーネント間の不整合 ● 例: Deno Deployでユーザーのコードが console.log などで吐いた ログが保存されていることを確認するテスト ○ 複数のレイヤーでバッファリングされる (runner, origin, etc.) ○ console.log を実行した時刻 t1、ログストレージに格納される時 刻 t2 の関係について、不正確な仮定のもとテストを書くとflakyにな る (1秒sleepするとか)
2. 非同期で動く複数コンポーネント間の不整合 ● 例: Deno Deployでユーザーのコードが console.log などで吐いた ログが保存されていることを確認するテスト ○ 複数のレイヤーでバッファリングされる (runner, origin, etc.) ○ console.log を実行した時刻 t1、ログストレージに格納される時 刻 t2 の関係について、不正確な仮定のもとテストを書くとflakyにな る (1秒sleepするとか) ● 対策: 期待される結果が得られるまで、複数回リトライする
2. 非同期で動く複数コンポーネント間の不整合 ● 例: Deno Deployでユーザーのコードが console.log などで吐いた ログが保存されていることを確認するテスト ● 対策1: 期待される結果が得られるまで、複数回リトライする ○ 無限リトライする場合は必ずタイムアウトを設ける ○ #[flaky_test] マクロ from flaky_test クレート
2. 非同期で動く複数コンポーネント間の不整合 ● 例: Deno Deployでユーザーのコードが console.log などで吐いた ログが保存されていることを確認するテスト ● 対策2: 各コンポーネントで、キーとなるアクション (e.g. ログのflush) が 実行されたときに標準出力などにログを吐くようにして、それをもとに同期 をとる 💡 とにかく雑な tokio::time::sleep を避けるようにする
3. 依存ライブラリ内の実装ミス
3. 依存ライブラリ内の実装ミス ● 例: 同じホストに対して fetch を大量の並行で実行するとき、たまに HTTP/2のストリームに関するエラーが起きる
3. 依存ライブラリ内の実装ミス ● 例: 同じホストに対して fetch を大量の並行で実行するとき、たまに HTTP/2のストリームに関するエラーが起きる ● 原因: Denoのfetchの実装で利用されていたRustのHTTPライブラリ reqwest の実装がまずかった ○ HTTP/2のコネクション確立後少しの間、サーバー側の MAX_CONCURRENT_STREAMS (同時に開くことが許されるストリー ム数) を超過してしまうことがありうる実装になっていた
3. 依存ライブラリ内の実装ミス ● 例: 同じホストに対して fetch を大量の並行で実行するとき、たまに HTTP/2のストリームに関するエラーが起きる ● 原因: Denoのfetchの実装で利用されていたRustのHTTPライブラリ reqwest の実装がまずかった ○ HTTP/2のコネクション確立後少しの間、サーバー側の MAX_CONCURRENT_STREAMS (同時に開くことが許されるストリー ム数) を超過してしまうことがありうる実装になっていた ● 対応: ライブラリにパッチを送った
flaky testの追跡と集計
flaky testの追跡と集計 ● そもそもflaky testがflakyである と認知するためには、複数回のテ ストスイート実行結果の保存、集 計、比較が必要 ● BuildPulseなどのSaaSがある ● cargo-nextestを使うとテスト結 果をJUnit形式で出力でき、あらゆ るSaaSと統合可能
もっと根本的な対策
テストはできるだけ smallに!
テストはできるだけ smallに! ● “Test Size” によるテスト分類 (by Google) において、Largeより Medium、MediumよりSmallな テストでカバーできないかを深く検 討する ● サイズを下げる方向にモックを活 用する https://testing.googleblog.com/2010/12/test-sizes.html
🦁 t-wadaさんのプレゼン とても学びが多いのでおすすめです https://levtech.jp/media/article/c olumn/detail_496/
俺たちの戦いはこれからだ! まだまだ大量にあるOpen状態なflaky ラベル付きissueたち 奮闘の日々は続く…… To be continued