Slide 1

Slide 1 text

Flaky Testへの現実解を Goのプロポーザルから考える Go Conference 2025 うぱ @upamune / LayerX

Slide 2

Slide 2 text

Flaky Test を書いてしまったことがある人〜?

Slide 3

Slide 3 text

Flaky Test、ありますよね? 「あー、それいつも落ちてるやつなんで無視していいですよ」 「リトライしたら通りませんか?」 © LayerX Inc. "普段はパスするけど、たまに落ちる" テスト CI/CDの生産性を大きく損なう存在 3

Slide 4

Slide 4 text

直した方がいい

Slide 5

Slide 5 text

直した方がいいんだけど... 様々な事情があり修正が難しい

Slide 6

Slide 6 text

Go本体にもFlaky Testは存在する golang/go の src/internal/testenv/testenv.go にFlaky Testをスキップするための以下が関数が定義されている © LayerX Inc. 6

Slide 7

Slide 7 text

Go本体にもFlaky Testは存在する カテゴリ 件数 主な原因 ネットワーク関連 17件 DNS解決、タイムアウト デバッガー統合 15件 GDB/LLDB挙動の不安定性 CGO関連 14件 シグナル処理、スレッド管理 Windows固有 10件 シグナル、SEH 合計85件の testenv.SkipFlaky か testenv.SkipFlakyNet でマークされたテスト go test ではこのFlaky Testは実行されず、 go test -flaky フラグがある時は実行される。 golang/go@9b2d39b75b で rg "testenv\.SkipFlaky(Net)?" -t go | wc -l をした結果 © LayerX Inc. 7

Slide 8

Slide 8 text

Go本体のFlaky Test 例えば... © LayerX Inc. #54503: src/cmd/go/internal/modfetch/coderepo_test.go Goモジュールのコードリポジトリからバージョン情報や内容を正しく取得できることを確認するテストが gopkg.in に依存していてFlaky #29225: src/net/udpsock_test.go UDPで0バイトのペイロードを送受信できることを確認するテストがiOSでFlaky #50470: src/time/sleep_test.go タイマーを急速に過去の時刻に変更しても、タイマーがドロップされないことを確認するテストがPlan 9 (arm) でFlaky 8

Slide 9

Slide 9 text

Go にもFlaky Test をハンドリングするための プロポーザルが

Slide 10

Slide 10 text

2018年:Issue #27181 2023年:Issue #62244 © LayerX Inc. golang/go へのFeature Request / Proposal Flaky Test支援のFeature Request → 却下 Flaky Testを明示的にマークし、 go test コマンドがそれを自動的に再試行する機能 「不安定なテストを許容し、助長するような機能を公式ツールに追加することには反 対」 @bradfitz がFlaky Test支援のプロポーザルを作成 → 承認 Google社内ではBazelのビルドシステムがFlaky Testの検出・アノテーション機能を持っ ているので困らないらしい Tailscaleでは go test のwrapperを作っていてリトライとアノテーションができる 10

Slide 11

Slide 11 text

承認されたプロポーザルの内容 © LayerX Inc. golang/go へのFeature Request / Proposal 既知のFlaky Testを明示的にマーク マークするだけで即失敗とはならないような形で合意された Flaky Test をただマークする方法だと、すべてのテストで画一的なリトライになった り、再実行までの時間を変えたりする柔軟性がない Retries() をハンドリングすることで、exponential にsleepするテストを作れたりする 11

Slide 12

Slide 12 text

Tailscale の go test の wrapper は何をやっている?

Slide 13

Slide 13 text

Tailscaleのtestwrapper/flakytestのアプローチ:利用側 © LayerX Inc. Tailscale の go test の wrapper flakytest.Mark でFlaky Testをマーキング Tailscale のリポジトリのIssue のURLであることを正規表現で確認 (ref) 正規表現に沿っていなかったら t.Fatal 13

Slide 14

Slide 14 text

Tailscaleのtestwrapper/flakytestのアプローチ © LayerX Inc. Tailscale の go test の wrapper t.Log("flakytest: this is a known flaky test") でログを出力 cmd/testwrapper がログの出力から文字列前方一致でFlaky Testであるかどうかを判定 Flaky Test だったら失敗したら再実行 14

Slide 15

Slide 15 text

余談:Go 1.25 なら t.Attrが使える Go 1.25から t.Attr が利用可能になったので、ログの出力を文字列マッチしなくても良くなった © LayerX Inc. Tailscale の go test の wrapper 15

Slide 16

Slide 16 text

今からFlaky Test に対処するには...?

Slide 17

Slide 17 text

今からFlaky Test に対処するには...? © LayerX Inc. Flaky Testを直す Go 1.25 からは synctest パッケージも正式追加されたので非同期処理にまつわるFlaky Testは直しやすくなった deep dive into testing/synctest・Go1.25新機能 testing/synctest で高速&確実な並列テストを実現する方法 失敗したテストをとにかく再実行したい gotestyourself/gotestsum を使えば --rerun-fails を使える 他にも類似ツールはありそう マークした特定のFlaky Testを再実行したい 紹介したTailscaleのtestwrapperを移植して使うのがおすすめ LayerX でも移植して利用していました (いました...?) 17

Slide 18

Slide 18 text

まとめ

Slide 19

Slide 19 text

まとめ Flaky Test は頑張って直しましょう © LayerX Inc. Flaky Test に対処できるプロポーザルがGoに承認された 実際にFlaky Test 再実行したい場合は現時点だといくつかのアプローチが取れる 全体を雑に再実行 落ちたテストだけを再実行 マーキングしたテストだけを再実行 マーキングしたテストだけを再実行したい場合は、Tailscaleの testwrapper は参考にな る実装なので見るのおすすめ 19

Slide 20

Slide 20 text

ありがとうございました! @upamune