[インデックス 16448] ファイルの概要
このコミットは、Go言語の標準ライブラリ testing パッケージに、ベンチマークテストの実行回数を調整する roundDown10 関数に関連するバグ(Issue 5599)をテストするための新しいテストケースを追加するものです。具体的には、src/pkg/testing/benchmark_test.go に TestRoundDown10 というテスト関数と、そのテストデータ roundDownTests が追加され、src/pkg/testing/export_test.go には内部関数 roundDown10 をテスト可能にするためのエクスポートが追加されています。
コミット
commit 787976c73936e5e19535872f570116a6852c7c6a
Author: Dave Cheney <dave@cheney.net>
Date: Fri May 31 23:03:22 2013 +1000
testing: add test for issue 5599
Update #5599
R=golang-dev, r, minux.ma
CC=golang-dev
https://golang.org/cl/9738052
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/787976c73936e5e19535872f570116a6852c7c6a
元コミット内容
testing: add test for issue 5599
Update #5599
R=golang-dev, r, minux.ma
CC=golang-dev
https://golang.org/cl/9738052
変更の背景
このコミットの背景には、Go言語の testing パッケージにおけるベンチマークテストの実行回数調整に関するバグ、具体的には Issue 5599: testing: Benchmark.N can be 0 が存在します。
Goのベンチマークテストは、testing.B 型の N フィールドを使用して、テスト対象の操作を何回繰り返すかを決定します。N の値は、ベンチマークの実行時間に基づいて動的に調整され、通常は10の倍数に丸められます。この丸め処理を行う内部関数が roundDown10 です。
Issue 5599では、roundDown10 関数が特定の入力値(例えば101や1001)に対して期待される結果(それぞれ100や1000)を返さず、誤った丸め処理を行う可能性が指摘されました。これにより、ベンチマークの実行回数が意図せず0になるなど、ベンチマークテストの信頼性に影響を与える可能性がありました。
このコミットは、この roundDown10 関数のバグを再現し、修正を検証するためのテストケースを追加することを目的としています。テストケースを追加することで、将来的な回帰を防ぎ、関数の正確性を保証します。
前提知識の解説
Go言語の testing パッケージ
Go言語には、ユニットテスト、ベンチマークテスト、サンプルテストをサポートするための組み込みの testing パッケージがあります。
- ユニットテスト:
TestXxxという形式の関数で記述され、コードの個々の単位が正しく動作するかを検証します。 - ベンチマークテスト:
BenchmarkXxxという形式の関数で記述され、コードのパフォーマンスを測定します。testing.B型の引数を受け取り、b.N回のループ内でテスト対象の操作を実行します。b.Nは、ベンチマークの実行時間に基づいて自動的に調整されます。 testing.B.N: ベンチマーク関数内で実行されるループの繰り返し回数を示します。Goのテストフレームワークは、ベンチマークが少なくとも1秒間実行されるようにb.Nの値を調整します。この調整プロセスには、実行回数を10の倍数に丸める処理が含まれます。
roundDown10 関数
testing パッケージの内部には、ベンチマークの実行回数 N を調整するために使用される roundDown10 という関数が存在します。この関数の目的は、与えられた数値を10の累乗(1, 10, 100, 1000など)に切り下げることです。例えば、roundDown10(123) は 100 を返し、roundDown10(9) は 1 を返します。これは、ベンチマークの実行回数を「きれいな」数値に保ち、結果の解釈を容易にするためです。
Goのテスト慣習と _test.go ファイル
Goでは、テストコードは通常、テスト対象のソースファイルと同じディレクトリに _test.go というサフィックスを持つファイルとして配置されます。
package <name>: 通常のテストファイルは、テスト対象のパッケージと同じパッケージ名を使用します。これにより、テストコードはテスト対象のパッケージの内部関数や変数にアクセスできます。package <name>_test: ベンチマークテストや、テスト対象のパッケージの外部から見た振る舞いをテストしたい場合は、_testサフィックスを付けたパッケージ名を使用します。これにより、テストコードはテスト対象のパッケージをインポートして使用することになり、外部APIのみにアクセスできます。
export_test.go ファイル
Goでは、パッケージの内部関数や変数は通常、外部から直接アクセスできません。しかし、テスト目的で内部関数にアクセスしたい場合があります。このような場合、Goのテストフレームワークは export_test.go という特別なファイル名パターンを認識します。このファイル内で、内部関数や変数をパッケージレベルの変数に代入することで、テストパッケージからそれらにアクセスできるようになります。これは、テストのためだけにAPIを公開するものであり、通常のアプリケーションコードからは使用されません。
技術的詳細
このコミットは、roundDown10 関数の正確性を検証するために、以下の技術的アプローチを取っています。
-
roundDown10関数のエクスポート:src/pkg/testing/export_test.goファイルが新規作成され、testingパッケージの内部関数roundDown10がRoundDown10という名前でエクスポートされています。package testing var RoundDown10 = roundDown10これにより、
testing_testパッケージ(つまり、testingパッケージの外部からテストを行うパッケージ)からtesting.RoundDown10としてこの関数を呼び出すことが可能になります。これは、内部関数のテストを可能にするための標準的なGoのテストパターンです。 -
新しいベンチマークテストファイルの追加:
src/pkg/testing/benchmark_test.goファイルが新規作成され、testing_testパッケージの一部として定義されています。package testing_test import ( "testing" )このファイルは、
testingパッケージをインポートし、そのエクスポートされた関数RoundDown10を使用してテストを行います。 -
テストデータの定義:
benchmark_test.go内に、roundDownTestsという構造体のスライスが定義されています。これは、roundDown10関数に与える入力値vと、それに対して期待される出力値expectedのペアを保持します。var roundDownTests = []struct { v, expected int }{ {1, 1}, {9, 1}, {10, 1}, {11, 10}, {100, 10}, // {101, 100}, // issue 5599 {1000, 100}, // {1001, 1000}, // issue 5599 }注目すべきは、コメントアウトされた
{101, 100}と{1001, 1000}の行です。これらは、Issue 5599で指摘された、roundDown10が正しく動作しない可能性のある具体的な入力値と、その期待される結果を示しています。テストが追加された時点では、これらのケースはまだ失敗する可能性があったため、コメントアウトされています。これは、バグが修正された後にコメントを解除してテストを有効にするためのプレースホルダーとして機能します。 -
テスト関数の実装:
TestRoundDown10というテスト関数が実装されています。この関数はroundDownTestsスライスをイテレートし、各テストケースに対してtesting.RoundDown10を呼び出し、実際の結果が期待される結果と一致するかを検証します。func TestRoundDown10(t *testing.T) { for _, tt := range roundDownTests { actual := testing.RoundDown10(tt.v) if tt.expected != actual { t.Errorf("roundDown10: expected %v, actual %v", tt.expected, actual) } } }もし期待される結果と実際の結果が異なる場合、
t.Errorfを使用してエラーメッセージを出力し、テストを失敗させます。
このコミットは、特定のバグをターゲットにしたテスト駆動開発(TDD)のアプローチを示しています。まずバグを再現するテストケースを作成し、そのテストが失敗することを確認します。その後、バグを修正するコードを書き、テストが成功することを確認します。これにより、バグが修正されたことを保証し、将来的な回帰を防ぐことができます。
コアとなるコードの変更箇所
このコミットによるコードの変更は以下の2つのファイルに集中しています。
-
src/pkg/testing/benchmark_test.go(新規追加)roundDownTestsというテストデータスライスの定義。TestRoundDown10というテスト関数の定義。
-
src/pkg/testing/export_test.go(新規追加)testingパッケージの内部関数roundDown10をRoundDown10としてエクスポートする行。
src/pkg/testing/benchmark_test.go
--- /dev/null
+++ b/src/pkg/testing/benchmark_test.go
@@ -0,0 +1,31 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package testing_test
+
+import (
+ "testing"
+)
+
+var roundDownTests = []struct {
+ v, expected int
+}{
+ {1, 1},
+ {9, 1},
+ {10, 1},
+ {11, 10},
+ {100, 10},
+ // {101, 100}, // issue 5599
+ {1000, 100},
+ // {1001, 1000}, // issue 5599
+}
+
+func TestRoundDown10(t *testing.T) {
+ for _, tt := range roundDownTests {
+ actual := testing.RoundDown10(tt.v)
+ if tt.expected != actual {
+ t.Errorf("roundDown10: expected %v, actual %v", tt.expected, actual)
+ }
+ }
+}
src/pkg/testing/export_test.go
--- /dev/null
+++ b/src/pkg/testing/export_test.go
@@ -0,0 +1,7 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package testing
+
+var RoundDown10 = roundDown10
コアとなるコードの解説
src/pkg/testing/benchmark_test.go
このファイルは、testing パッケージのベンチマーク関連機能、特に roundDown10 関数の動作を検証するためのテストケースを定義しています。
package testing_test: この行は、このファイルがtestingパッケージの外部テストであることを示しています。これにより、testingパッケージをインポートして、そのエクスポートされた機能(この場合はtesting.RoundDown10)のみを使用できます。これは、パッケージの公開APIが正しく動作するかを検証する標準的な方法です。var roundDownTests = []struct { v, expected int }: これは、テーブル駆動テスト(Table Driven Tests)の典型的なパターンです。vはroundDown10関数への入力値、expectedはその入力に対する期待される出力値を表します。{1, 1},{9, 1},{10, 1}: 10未満の数値や10自体が正しく1に丸められることをテストします。{11, 10},{100, 10}: 10より大きく100未満の数値が正しく10に丸められることをテストします。{1000, 100}: 1000が正しく100に丸められることをテストします。// {101, 100}, // issue 5599と// {1001, 1000}, // issue 5599: これらは、Issue 5599で報告された具体的なバグのケースです。これらの行がコメントアウトされているのは、コミット時点ではまだバグが修正されておらず、テストが失敗する可能性があるためです。バグが修正された後、これらのコメントを解除してテストを有効にすることで、回帰テストとして機能します。
func TestRoundDown10(t *testing.T): これはGoのテスト関数です。t *testing.T引数は、テストの実行中にエラーを報告したり、テストの状態を制御したりするためのメソッドを提供します。for _, tt := range roundDownTests:roundDownTestsスライス内の各テストケースをループ処理します。actual := testing.RoundDown10(tt.v): エクスポートされたRoundDown10関数を呼び出し、実際の結果を取得します。if tt.expected != actual: 実際の結果が期待される結果と異なる場合、エラーを報告します。t.Errorf("roundDown10: expected %v, actual %v", tt.expected, actual): テストが失敗したことを示し、期待値と実際の値を含む詳細なエラーメッセージを出力します。
src/pkg/testing/export_test.go
このファイルは、testing パッケージの内部関数をテスト目的で外部に公開するために使用されます。
package testing: このファイルはtestingパッケージの一部として定義されています。var RoundDown10 = roundDown10: この行がこのファイルの核心です。testingパッケージ内で定義されている非エクスポート(小文字で始まる)関数roundDown10を、エクスポートされた(大文字で始まる)変数RoundDown10に代入しています。これにより、testingパッケージをインポートする他のパッケージ(例:testing_testパッケージ)からtesting.RoundDown10としてこの内部関数にアクセスできるようになります。これは、Goで内部関数をテストするための一般的なイディオムです。
これらの変更により、roundDown10 関数の正確性を体系的に検証するための基盤が確立され、Issue 5599のようなバグの再発を防ぐための重要なステップとなります。
関連リンク
- Go Issue 5599: testing: Benchmark.N can be 0
- Go Code Review 9738052: testing: add test for issue 5599 (このコミットに対応するGoのコードレビューページ)
参考にした情報源リンク
- Go のテスト - The Go Programming Language
- Go のベンチマークテスト - The Go Programming Language
- Go の内部関数をテストする方法 (export_test.go) (類似の概念を説明する記事)
- Go のテーブル駆動テスト (テーブル駆動テストのパターンに関するGo公式ブログ)