[インデックス 11927] ファイルの概要
コミット
commit 9a445600334fcd4e856206b0223f8b85c71f7999
Author: Alex Brainman <alex.brainman@gmail.com>
Date: Wed Feb 15 14:56:47 2012 +1100
time: run TestTicker for longer during short test, otherwise it fails
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5671049
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/9a445600334fcd4e856206b0223f8b85c71f7999
元コミット内容
time: run TestTicker for longer during short test, otherwise it fails
このコミットは、Go言語の標準ライブラリtime
パッケージ内のTestTicker
テストが、go test -short
フラグを付けて実行された場合に失敗する問題を修正するためのものです。具体的には、testing.Short()
がtrue
の場合にTestTicker
が使用するDelta
(ティック間隔)の値を10 * Millisecond
から20 * Millisecond
に倍増させることで、テストの安定性を向上させています。
変更の背景
Go言語のテストフレームワークには、go test -short
というフラグがあります。これは、開発者が時間のかかるテストやリソースを大量に消費するテストをスキップし、より迅速なテスト実行を可能にするためのものです。テストコード内でtesting.Short()
関数を呼び出すことで、このフラグが設定されているかどうかを判定し、テストの動作を調整できます。
time
パッケージのTestTicker
は、time.Ticker
の機能が正しく動作するかを検証するテストです。time.Ticker
は、指定された間隔でイベントを定期的に発生させるためのメカニズムを提供します。このテストでは、Count
回イベントが発生するのを待ち、その間隔が期待通りであることを検証します。
問題は、go test -short
モードで実行された際に、TestTicker
がDelta
の値を10 * Millisecond
に設定していたことにありました。この10ミリ秒
という非常に短い間隔は、テストが実行される環境(CPUの負荷、スケジューリング、システムコールにかかる時間など)によっては、正確なティックを検出するには短すぎる場合がありました。結果として、テストが期待されるCount
回のティックを時間内に受け取れず、テストが不安定になったり、ランダムに失敗したりする「flaky test(不安定なテスト)」の状態に陥っていました。
このコミットは、go test -short
モードでのTestTicker
の信頼性を向上させることを目的としています。Delta
の値を20ミリ秒
に増やすことで、テストがより多くの猶予を持ち、様々な実行環境下でも安定してパスするように調整されています。
前提知識の解説
1. time.Ticker
Go言語のtime
パッケージは、時間に関連する機能を提供します。その中でもtime.Ticker
は、一定の間隔でイベントを繰り返し発生させるための構造体です。
time.NewTicker(d Duration)
: 指定された期間d
ごとにティックを送信する新しいTicker
を作成します。Ticker.C <-chan Time
:Ticker
構造体にはC
という名前のチャネルが含まれています。このチャネルは、ティックが発生するたびに現在の時刻を送信します。ユーザーはこのチャネルを読み取ることで、定期的なイベントを処理できます。Ticker.Stop()
:Ticker
の使用が終わったら、必ずStop()
メソッドを呼び出してリソースを解放する必要があります。これを怠ると、関連するゴルーチンがリークする可能性があります。
time.Ticker
は、例えば定期的なログの出力、キャッシュの更新、ポーリング処理など、一定間隔で処理を実行したい場合に非常に便利です。
2. testing.Short()
Go言語のテストフレームワーク(testing
パッケージ)には、テストの実行を制御するための便利な機能がいくつかあります。その一つがtesting.Short()
関数です。
- 目的:
testing.Short()
は、go test
コマンドが-short
フラグ付きで実行された場合にtrue
を返します。この機能は、開発者が時間のかかるテストや、外部リソース(ネットワーク、データベースなど)に依存するテストを、通常の開発サイクルではスキップし、CI/CDパイプラインなどのより包括的なテスト実行時にのみ実行できるようにするために使用されます。 - 使用例:
このように記述することで、func TestLongRunningOperation(t *testing.T) { if testing.Short() { t.Skip("skipping test in short mode.") } // 時間のかかるテストロジック }
go test -short
と実行した際にはTestLongRunningOperation
はスキップされ、go test
と実行した際には通常通り実行されます。 - テスト動作の変更:
testing.Short()
は、テストをスキップするだけでなく、テストの動作自体を「ショートモード」用に変更するためにも使用できます。例えば、テストが実際の外部サービスと通信する代わりに、モックデータや簡略化されたセットアップを使用するように切り替えることができます。今回のコミットは、この「テスト動作の変更」の典型的な例であり、テストのタイミングパラメータを調整しています。
3. Goテストにおけるタイミングの問題
Goのテスト、特に並行処理や時間に関連するテストでは、タイミングの問題(flaky tests)が発生しやすいことがあります。
time.Sleep
の信頼性の低さ: テスト内でtime.Sleep
を使用してゴルーチンの完了やイベントの発生を待つことは、非常に信頼性が低いです。実際のスリープ時間は、システムスケジューラ、OSの違い、システム負荷などによって変動する可能性があり、テスト結果の一貫性を損ないます。- 競合状態(Race Conditions): 複数のゴルーチンが同じ変数に同時にアクセスし、少なくとも1つが書き込みである場合に発生します。これはテストの不安定性の主要な原因の一つです。Goには
go test -race
という競合検出ツールがあり、これを使用することで競合状態を特定できます。 - 非決定的な動作: マップのイテレーション順序が非決定的であるなど、Go言語の特定の動作がテストの非決定性を引き起こすことがあります。
- 解決策:
time.Sleep
の代わりに、sync.WaitGroup
、チャネル、sync.Cond
などの適切な同期メカニズムを使用して、ゴルーチンの完了やイベントの発生を待つべきです。- Go 1.24以降では、実験的な
testing/synctest
パッケージが導入され、時間依存のテストをより決定的に、かつ高速に実行できるようになりました。 - テストが時間測定機能を検証する場合、実際の経過時間に基づいて上限と下限を計算し、その範囲内であることをアサートします。
- テストのタイムアウトは、
go test -timeout
フラグで設定できますが、これはテストロジックの一部としてではなく、安全策として使用すべきです。
今回のコミットは、まさにこの「テストにおけるタイミングの問題」に対処するものであり、特にtesting.Short()
モードでのテストの安定性を確保するために、テストのタイミングパラメータを調整しています。
技術的詳細
TestTicker
は、time.Ticker
が正確な間隔でティックを生成するかどうかを検証します。テストのロジックは以下のようになっています。
Count
(10回)のティックを期待します。Delta
というティック間隔を定義します。testing.Short()
がtrue
の場合(つまり、go test -short
で実行された場合)、Delta
の値を短く設定します。NewTicker(Delta)
でTicker
を作成します。Count
回、ticker.C
チャネルからティックを受信します。- ティックを受信するたびに、前回のティックからの経過時間を測定し、それが
Delta
の期待される範囲内にあることを検証します。 - 全てのティックを受信した後、
ticker.Stop()
を呼び出してTicker
を停止します。
変更前のコードでは、testing.Short()
がtrue
の場合にDelta
が10 * Millisecond
に設定されていました。これは非常に短い時間であり、特にテストが実行される環境の負荷が高い場合や、システムコールやゴルーチンのスケジューリングにわずかな遅延が生じた場合に、テストが期待される10ミリ秒
間隔で正確にティックを受信できない可能性がありました。
例えば、テストが10ミリ秒
のティックを期待しているにもかかわらず、システムが11ミリ秒
かかって次のティックを生成した場合、テストは失敗します。これは、テストが厳密すぎるか、またはテスト環境が不安定であるために発生します。
このコミットでは、Delta
を20 * Millisecond
に倍増させることで、テストがより寛容になります。これにより、システムがティックを生成するのにわずかに時間がかかったとしても、テストが失敗する可能性が低減されます。これは、テストのロバスト性(堅牢性)を高め、環境依存の不安定なテスト(flaky test)を減らすための一般的なアプローチです。テストの目的は機能の正確性を検証することであり、極端なタイミングの正確性を検証することではない場合、このような調整は適切です。
コアとなるコードの変更箇所
--- a/src/pkg/time/tick_test.go
+++ b/src/pkg/time/tick_test.go
@@ -13,7 +13,7 @@ func TestTicker(t *testing.T) {
const Count = 10
Delta := 100 * Millisecond
if testing.Short() {
- Delta = 10 * Millisecond
+ Delta = 20 * Millisecond
}
ticker := NewTicker(Delta)
t0 := Now()
コアとなるコードの解説
このコミットの変更は、src/pkg/time/tick_test.go
ファイル内のTestTicker
関数にあります。
元のコードでは、testing.Short()
がtrue
の場合、つまりgo test -short
フラグが指定されている場合に、Delta
変数が10 * Millisecond
に設定されていました。
if testing.Short() {
Delta = 10 * Millisecond
}
この変更により、Delta
の値が20 * Millisecond
に修正されました。
if testing.Short() {
- Delta = 10 * Millisecond
+ Delta = 20 * Millisecond
}
この変更の意図は、go test -short
モードでTestTicker
が実行される際のティック間隔を長くすることです。10ミリ秒
という短い間隔では、テストが実行される環境の特性(例えば、CPUのスケジューリングの遅延、システムコールのオーバーヘッドなど)によって、期待されるティックが正確なタイミングで発生しないことがありました。これにより、テストが不安定になり、ランダムに失敗する原因となっていました。
Delta
を20ミリ秒
に増やすことで、各ティック間の許容される誤差が実質的に大きくなります。これにより、テストはより多くの猶予を持ち、わずかなシステム遅延があっても安定してパスするようになります。これは、テストの信頼性を向上させ、開発者がテスト結果に一貫性を持って信頼できるようにするための重要な調整です。
関連リンク
- Go言語のコミット: https://golang.org/cl/5671049
- GitHub上のコミットページ: https://github.com/golang/go/commit/9a445600334fcd4e856206b0223f8b85c71f7999
参考にした情報源リンク
- Go言語の
testing.Short()
に関する説明: - Go言語の
time.Ticker
に関する説明: - Go言語のテストにおけるタイミングの問題とflaky testsに関する議論: