Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 12424] ファイルの概要

このコミットは、Go言語の標準ライブラリであるtimeパッケージ内のtick_test.goファイルに対する変更です。具体的には、TestTickerというテスト関数において、go test -shortフラグが指定された「ショートテスト」実行時に、time.Tickerの動作が期待よりも遅延した場合でもテストが失敗しないように修正されています。これにより、開発時の迅速なテスト実行におけるテストの不安定性(flakiness)が軽減されます。

コミット

commit db80edde7ddd144388e367c9a8328121c98330ce
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Wed Mar 7 01:10:55 2012 +0800

    time: during short test, do not bother tickers take longer than expected
    
    R=golang-dev, bradfitz
    CC=golang-dev
    https://golang.org/cl/5752058

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/db80edde7ddd144388e367c9a8328121c98330ce

元コミット内容

このコミットは、timeパッケージのtick_test.goファイルにおいて、TestTicker関数内のアサーションロジックを修正しています。元のコードでは、time.Tickerが生成するティックの間隔が期待される範囲(target-slopからtarget+slop)を外れた場合にテストが失敗していました。

変更の背景

Go言語のテストフレームワークには、テストの実行時間を短縮するための-shortフラグが存在します。このフラグが指定されると、testing.Short()関数がtrueを返します。通常、開発者はこのフラグを使用して、CI/CDパイプラインでのフルテスト実行とは別に、ローカルでの迅速な開発サイクル中に時間のかかるテストをスキップします。

time.Tickerのような時間ベースのテストは、実行環境の負荷、OSのスケジューリング、ガベージコレクションの発生など、様々な外部要因によってそのタイミングが微妙に変動する可能性があります。特に、期待よりも時間がかかった場合(dt > target+slop)にテストが失敗するケースは、これらの外部要因によってテストが不安定(flaky)になる原因となりがちでした。

このコミットの背景には、ショートテストの目的は基本的な機能の検証であり、厳密なタイミング精度を保証することではない、という考えがあります。厳密なタイミングテストは、フルテスト実行時にのみ行われるべきであり、ショートテストでは多少の遅延は許容することで、開発者の生産性を向上させ、テストの信頼性を高めることが目的です。

前提知識の解説

Go言語のtestingパッケージと-shortフラグ

Go言語には、標準でテストを記述・実行するためのtestingパッケージが提供されています。

  • go test: Goのテストを実行するためのコマンドです。
  • testing.T: 各テスト関数に渡される構造体で、テストの失敗報告、ログ出力、ヘルパー関数の呼び出しなど、テスト実行に関する様々な機能を提供します。
  • testing.Short(): この関数は、go testコマンドに-shortフラグが渡された場合にtrueを返します。開発者はこの関数を使って、時間のかかるテストやリソースを大量に消費するテストを条件付きでスキップすることができます。例えば、データベース接続を必要とするテストや、ネットワーク通信を伴うテストなどで利用されます。

Go言語のtimeパッケージとtime.Ticker

  • timeパッケージ: 日付と時刻に関する機能を提供するGoの標準パッケージです。
  • time.Duration: 時間の長さを表す型です。ナノ秒単位で表現され、time.Secondtime.Millisecondなどの定数を使って期間を指定できます。
  • time.Ticker: 定期的にイベントを発生させるためのメカニズムを提供します。time.NewTicker(d Duration)で作成され、指定された期間dごとにチャネルに値を送信します。これにより、一定間隔で処理を実行するようなアプリケーションやテストで利用されます。

テストの不安定性(Flakiness)

ソフトウェアテストにおける「不安定性(flakiness)」とは、コードの変更がないにもかかわらず、テストが成功したり失敗したりする現象を指します。これは、テストが外部要因(実行環境の負荷、ネットワークの遅延、並行処理のタイミング、乱数の初期化など)に依存している場合に発生しやすく、開発者の信頼を損ない、CI/CDパイプラインの効率を低下させる大きな問題となります。時間ベースのテストは、特にこの不安定性の影響を受けやすい傾向があります。

技術的詳細

このコミットの技術的な核心は、Goのテストフレームワークが提供するtesting.Short()関数を、時間ベースのテストの許容範囲を動的に調整するために利用している点にあります。

元のコードでは、TestTicker関数はtime.Tickerの動作を検証しており、実際の経過時間dtが期待される時間targetから許容誤差slopを引いた値よりも小さいか、またはtargetslopを加えた値よりも大きい場合にテストを失敗させていました。

if dt < target-slop || dt > target+slop {
    t.Fatalf(...)
}

この条件式は、dtが期待される時間範囲[target-slop, target+slop]の外にある場合に真となります。

変更後のコードでは、dt > target+slopという条件に!testing.Short()という条件が追加されました。

if dt < target-slop || (!testing.Short() && dt > target+slop) {
    t.Fatalf(...)
}

この変更により、条件式は以下のように解釈されます。

  1. dt < target-slop: 実際の経過時間dtが期待される最小値よりも短い場合。この条件は常に評価され、ショートテストかどうかにかかわらず、テストは失敗します。これは、Tickerが期待よりも早くティックを生成することは通常ありえないため、もし発生すればそれはバグである可能性が高いからです。

  2. (!testing.Short() && dt > target+slop):

    • testing.Short()trueの場合(go test -short実行時): !testing.Short()falseとなります。論理AND演算子&&の短絡評価(short-circuit evaluation)により、dt > target+slopの部分は評価されません。結果として、dt > target+slopの条件は無視され、テストはdtが期待よりも長かったとしても失敗しません。
    • testing.Short()falseの場合(通常のgo test実行時): !testing.Short()trueとなります。この場合、dt > target+slopの条件が評価されます。もしdtが期待される最大値よりも長ければ、この条件が真となり、テストは失敗します。

このロジックにより、ショートテスト実行時には、Tickerが期待よりも遅延した場合のチェックがスキップされ、テストの不安定性が解消されます。一方で、フルテスト実行時には、厳密なタイミング精度が引き続き検証されます。これは、テストの目的と実行コンテキストに応じて、適切な厳密さを適用する賢明なアプローチと言えます。

コアとなるコードの変更箇所

変更はsrc/pkg/time/tick_test.goファイルの一箇所のみです。

--- a/src/pkg/time/tick_test.go
+++ b/src/pkg/time/tick_test.go
@@ -22,7 +22,7 @@ func TestTicker(t *testing.T) {
  	dt := t1.Sub(t0)
  	target := Delta * Count
  	slop := target * 2 / 10
-	if dt < target-slop || dt > target+slop {
+	if dt < target-slop || (!testing.Short() && dt > target+slop) {
  		t.Fatalf("%d %s ticks took %s, expected [%s,%s]", Count, Delta, dt, target-slop, target+slop)
  	}
  	// Now test that the ticker stopped

コアとなるコードの解説

変更された行は、TestTicker関数内のif文の条件式です。

  • dt := t1.Sub(t0): テスト開始時刻t0から終了時刻t1までの実際の経過時間dtを計算しています。
  • target := Delta * Count: Tickerが生成するティックの期間Deltaとティックの回数Countから、期待される合計経過時間targetを計算しています。
  • slop := target * 2 / 10: 許容される誤差範囲slopを計算しています。ここでは、targetの20%が許容誤差として設定されています。
  • if dt < target-slop || (!testing.Short() && dt > target+slop):
    • dt < target-slop: 実際の経過時間dtが、期待される最小時間target-slopよりも短い場合。これは、Tickerが期待よりも早く動作した場合を検知します。この条件は常に評価されます。
    • (!testing.Short() && dt > target+slop): 実際の経過時間dtが、期待される最大時間target+slopよりも長い場合。これは、Tickerが期待よりも遅く動作した場合を検知します。この条件は、testing.Short()false(つまり、ショートテストではない)の場合にのみ評価されます。

この修正により、go test -short実行時には、Tickerが期待よりも遅延してもテストは失敗しなくなります。これにより、開発時のテストの安定性が向上し、CI/CDなどでのフルテスト実行時には引き続き厳密なタイミングチェックが行われるようになります。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Go言語のテストに関する一般的なプラクティスとtesting.Short()の利用例
  • ソフトウェアテストにおける「flakiness」に関する一般的な情報