File tree Expand file tree Collapse file tree 3 files changed +89
-0
lines changed Expand file tree Collapse file tree 3 files changed +89
-0
lines changed Original file line number Diff line number Diff line change 1+ # これは何?
2+
3+ Go1.24で実験的機能として追加され、Go1.25で一般提供となった [ testing.synctest] ( https://pkg.go.dev/testing/synctest ) パッケージについてのサンプルが配置されています。
4+
5+ ## bubble について
6+
7+ ** bubble** とは、テスト中に生成したgoroutineやtime.Timerなどのリソースを外部と完全に隔離した実行環境を意味します。
8+
9+ ### bubbleの特徴
10+
11+ #### 隔離された実行環境
12+
13+ synctest.Test()で作成されるbubble内で起動したgoroutineや生成したチャネル、タイマーなどは、bubble外から操作できません。たとえば、bubble内で作ったチャネルをbubble外から送受信しようとするとpanicになります。
14+
15+ #### 専用のfake時間
16+
17+ bubble内ではtimeパッケージがfake clock(模擬時計)を使います。時間はbubble内のgoroutineがすべて** durably blocked(耐久ブロック)** 状態になったときのみ進みます。つまり、計算処理中は時間が止まり、SleepやTimerの待ちが発生したときのみ時間が進みます。
18+
19+ #### durably blocked(耐久ブロック)
20+
21+ goroutineが「耐久ブロック」状態とは、そのgoroutineがbubble内の他のgoroutineからのみ解放される状態を指します。たとえば、bubble内のチャネルに対する送受信、sync.Cond.Wait、sync.WaitGroup.Wait、time.Sleepなどが該当します。
22+ 逆に、ファイルI/OやネットワークI/Oなど、bubble外の要因でブロックされる場合は「耐久ブロック」とはみなされません。
23+
24+ #### テストの安定性と高速化
25+
26+ bubbleを使うことで、goroutineの競合やタイミング依存によるテストの不安定さ(フレーキー)を防ぎつつ、実際には待たずに一瞬でテストが終わるため、高速かつ確実な非同期テストが可能になります。
27+
28+ ### bubbleの注意点
29+
30+ #### I/O系のブロックは対象外
31+
32+ ネットワークやファイルI/Oなど、カーネルや外部システムに依存するブロックは「耐久ブロック」とみなされず、bubbleの管理外です。そのため、bubble内でネットワーク通信をテストしたい場合は、fakeネットワーク(例:net.Pipe())を使う必要があります。
33+
34+ #### bubbleのライフサイクル
35+
36+ bubbleのルートgoroutine(Testに渡した関数)が終了すると、それ以降はfake時間も進まなくなり、bubble内のgoroutineがSleepやTimerでブロックしているとデッドロックでテストが失敗します。
37+
38+
39+ ## 参考情報
40+
41+ - [ synctest package] ( https://pkg.go.dev/testing/synctest )
42+ - [ Testing Time (and other asynchronicities)] ( https://go.dev/blog/testing-time )
43+ - [ Go synctest: Solving Flaky Tests] ( https://victoriametrics.com/blog/go-synctest/ )
Original file line number Diff line number Diff line change 1+ # https://taskfile.dev
2+
3+ version : ' 3'
4+
5+ tasks :
6+ default :
7+ cmds :
8+ - go test . -v
9+ ignore_error : true
Original file line number Diff line number Diff line change 1+ package faketime
2+
3+ import (
4+ "testing"
5+ "testing/synctest"
6+ "time"
7+
8+ "github.com/nalgeon/be"
9+ )
10+
11+ const (
12+ SleepTime = 2 * time .Second
13+ )
14+
15+ func TestNoFakeTime (t * testing.T ) {
16+ start := time .Now ()
17+ {
18+ // 普通に時間が流れるので指定時間スリープする
19+ time .Sleep (SleepTime )
20+ }
21+ elapsed := time .Since (start )
22+
23+ be .Equal (t , elapsed , SleepTime ) // 実際の結果は実行時に変化し、基本的に厳密な一致は出来ないことがほとんど
24+ }
25+
26+ func TestUseFakeTime (t * testing.T ) {
27+ synctest .Test (t , func (t * testing.T ) {
28+ start := time .Now ()
29+ {
30+ // Fake-Timeが使用されるためsynctest内では一瞬で完了する
31+ time .Sleep (SleepTime )
32+ }
33+ elapsed := time .Since (start )
34+
35+ be .Equal (t , elapsed , SleepTime ) // Fake-Timeにより結果が一致する
36+ })
37+ }
You can’t perform that action at this time.
0 commit comments