[インデックス 12487] ファイルの概要
このコミットは、Go言語の標準ライブラリであるsrc/pkg/testing/testing.go
ファイルに対する変更です。testing
パッケージは、Goプログラムの自動テスト、ベンチマーク、およびExample(使用例)の記述をサポートするための機能を提供します。このファイルは、go test
コマンドがテストやExampleを実行する際のコアロジックを含んでいます。
コミット
このコミットは、Goのtesting
パッケージにおいて、Exampleが存在する場合に「no tests to run」という警告が表示される問題を修正します。具体的には、テストが一つも存在しないがExampleが存在する場合に、不必要な警告が出力されないようにロジックが変更されました。
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/291636b99bcb581301faa14de61cde4c9093c335
元コミット内容
commit 291636b99bcb581301faa14de61cde4c9093c335
Author: Russ Cox <rsc@golang.org>
Date: Wed Mar 7 14:54:31 2012 -0500
testing: do not print 'no tests' when there are examples
I am not sure why RunTests and RunExamples are
exported, but I assume that because they are we
should not change the signature, so I added an
unexported global shared by Main and RunTests.
Fixes #3237.
R=golang-dev, gri
CC=golang-dev
https://golang.org/cl/5779043
変更の背景
Goのtesting
パッケージでは、go test
コマンドを実行した際に、テスト関数(TestXxx
)が一つも定義されていない場合に「testing: warning: no tests to run」という警告メッセージが標準エラー出力に表示されます。しかし、テスト関数は存在しないものの、Example関数(ExampleXxx
)が定義されている場合でも、この警告が表示されてしまうという問題がありました。
Example関数は、パッケージの使用例を示すための特別な関数で、go test
コマンドによって実行され、その出力が期待される出力と一致するかどうかが検証されます。Exampleはテストとは異なる目的を持つものの、go test
の実行対象であり、Exampleが存在するにも関わらず「テストがない」と警告されるのはユーザーにとって混乱を招く可能性がありました。
このコミットは、この不整合を解消し、Exampleが存在する場合には「no tests to run」警告が表示されないようにするために行われました。これはGo issue #3237として報告された問題の修正です。
前提知識の解説
Go言語のtesting
パッケージ
Go言語には、テストを記述するための組み込みのtesting
パッケージがあります。このパッケージは、ユニットテスト、ベンチマークテスト、およびExampleの作成をサポートします。
- テスト関数:
func TestXxx(t *testing.T)
というシグネチャを持つ関数で、Xxx
は英大文字で始まる任意の名前です。これらの関数は、コードの特定の単位が正しく動作するかどうかを検証するために使用されます。 - ベンチマーク関数:
func BenchmarkXxx(b *testing.B)
というシグネチャを持つ関数で、コードのパフォーマンスを測定するために使用されます。 - Example関数:
func ExampleXxx()
またはfunc ExampleXxx_Yyy()
というシグネチャを持つ関数で、パッケージや関数の使用方法を示すコード例を提供します。Example関数内のコードは実行され、その標準出力がコメント行のOutput:
セクションに記述された内容と一致するかどうかが検証されます。これにより、ドキュメントとコードの整合性が保たれます。
go test
コマンドの動作
go test
コマンドは、Goのソースコードディレクトリ内で実行され、そのディレクトリ内のテストファイル(ファイル名が_test.go
で終わるもの)をコンパイルし、テスト、ベンチマーク、Exampleを実行します。
go test
は内部的に、testing
パッケージのMain
関数を呼び出してテスト実行を管理します。Main
関数は、検出されたテスト、ベンチマーク、Exampleのリストを受け取り、それらを適切な順序で実行します。
testing
パッケージ内の主要な関数と変数(変更前)
Main(matchString func(pat, str string) (bool, error), tests []InternalTest, examples []InternalExample)
:go test
コマンドのエントリポイントとなる関数です。テスト、ベンチマーク、Exampleの実行をオーケストレートします。RunTests(matchString func(pat, str string) (bool, error), tests []InternalTest) (ok bool)
: 実際のテスト関数を実行するロジックを含みます。テストが一つも存在しない場合に「no tests to run」警告を出力していました。RunExamples(matchString func(pat, str string) (bool, error), examples []InternalExample) (ok bool)
: Example関数を実行するロジックを含みます。InternalTest
: 内部的にテスト関数を表す構造体です。InternalExample
: 内部的にExample関数を表す構造体です。
技術的詳細
この変更の核心は、testing
パッケージがExampleの存在を認識し、テストが存在しない場合でもExampleがあれば警告を出さないようにすることです。
-
新しいグローバル変数の導入:
testing
パッケージの内部に、haveExamples bool
という新しいグローバル変数が追加されました。この変数は、現在のテスト実行にExampleが含まれているかどうかを示すフラグとして機能します。bool
型であり、初期値はfalse
です。 -
Main
関数でのhaveExamples
の設定:Main
関数は、go test
コマンドによって検出されたExampleのリスト(examples []InternalExample
)を受け取ります。このコミットでは、Main
関数内でhaveExamples = len(examples) > 0
という行が追加されました。これにより、Exampleのリストが空でなければ(つまり、一つでもExampleが存在すれば)、haveExamples
がtrue
に設定されます。 -
RunTests
関数でのhaveExamples
の利用:RunTests
関数は、テスト関数が一つも存在しない場合に警告を出力するロジックを含んでいました。変更前はif len(tests) == 0
という条件で警告を出力していましたが、変更後はif len(tests) == 0 && !haveExamples
という条件に変更されました。 この新しい条件により、テスト関数が一つも存在しないかつExampleも存在しない場合にのみ警告が出力されるようになります。Exampleが存在する場合は、haveExamples
がtrue
になるため、!haveExamples
がfalse
となり、警告は表示されません。 -
RunTests
とRunExamples
のエクスポートに関する考察: コミットメッセージには「I am not sure why RunTests and RunExamples are exported, but I assume that because they are we should not change the signature」とあります。これは、これらの関数が外部にエクスポートされている(つまり、パッケージ外からアクセス可能である)ため、APIの互換性を保つためにそのシグネチャ(引数と戻り値の型)を変更すべきではないという開発者の判断を示しています。 もしシグネチャを変更できたのであれば、RunTests
関数にExampleの存在を示す引数を追加することも可能だったかもしれません。しかし、APIの安定性を優先し、既存のシグネチャを変更せずに、パッケージ内部で共有されるグローバル変数haveExamples
を導入するというアプローチが取られました。これにより、外部からRunTests
やRunExamples
を直接呼び出しているコードがあったとしても、そのコードに影響を与えることなく修正が適用されました。
この変更により、go test
の出力がより直感的になり、Exampleが存在する場合には「テストがない」という誤解を招く警告が抑制されるようになりました。
コアとなるコードの変更箇所
--- a/src/pkg/testing/testing.go
+++ b/src/pkg/testing/testing.go
@@ -107,6 +107,8 @@ var (
cpuListStr = flag.String("test.cpu", "", "comma-separated list of number of CPUs to use for each test")
parallel = flag.Int("test.parallel", runtime.GOMAXPROCS(0), "maximum test parallelism")
+ haveExamples bool // are there examples?
+
cpuList []int
)
@@ -279,6 +281,7 @@ func Main(matchString func(pat, str string) (bool, error), tests []InternalTest,
before()
startAlarm()
+ haveExamples = len(examples) > 0
testOk := RunTests(matchString, tests)
exampleOk := RunExamples(matchString, examples)
if !testOk || !exampleOk {
@@ -303,7 +306,7 @@ func (t *T) report() {
func RunTests(matchString func(pat, str string) (bool, error), tests []InternalTest) (ok bool) {
ok = true
- if len(tests) == 0 {
+ if len(tests) == 0 && !haveExamples {
fmt.Fprintln(os.Stderr, "testing: warning: no tests to run")
return
}
コアとなるコードの解説
src/pkg/testing/testing.go
の変更点:-
haveExamples
変数の追加:+ haveExamples bool // are there examples?
testing
パッケージのグローバル変数としてhaveExamples
が追加されました。これはbool
型で、Exampleが存在するかどうかを追跡するために使用されます。コメント// are there examples?
がその目的を明確に示しています。 -
Main
関数内でのhaveExamples
の設定:@@ -279,6 +281,7 @@ func Main(matchString func(pat, str string) (bool, error), tests []InternalTest, before() startAlarm() + haveExamples = len(examples) > 0 testOk := RunTests(matchString, tests) exampleOk := RunExamples(matchString, examples) if !testOk || !exampleOk {
Main
関数内で、haveExamples
変数が初期化されます。len(examples) > 0
という条件は、Main
関数に渡されたexamples
スライス(検出されたExample関数のリスト)の長さが0より大きい(つまり、Exampleが一つでも存在する)場合にtrue
となり、haveExamples
にその値が代入されます。これにより、RunTests
が呼び出される前にExampleの存在情報が設定されます。 -
RunTests
関数内での警告ロジックの変更:@@ -303,7 +306,7 @@ func (t *T) report() { func RunTests(matchString func(pat, str string) (bool, error), tests []InternalTest) (ok bool) { ok = true - if len(tests) == 0 { + if len(tests) == 0 && !haveExamples { fmt.Fprintln(os.Stderr, "testing: warning: no tests to run") return }
RunTests
関数内の警告出力の条件が変更されました。- 変更前:
if len(tests) == 0
テスト関数が一つも存在しない場合に警告を出力していました。 - 変更後:
if len(tests) == 0 && !haveExamples
テスト関数が一つも存在しないかつhaveExamples
がfalse
(つまり、Exampleも存在しない)の場合にのみ警告を出力するようになりました。これにより、Exampleが存在する場合には、テスト関数がなくても警告は表示されなくなります。
- 変更前:
-
これらの変更により、testing
パッケージはExampleの存在を考慮し、より適切な警告メッセージの表示を行うようになりました。
関連リンク
- Go issue #3237: このコミットが修正した問題のトラッキング。コミットメッセージに記載されているが、直接的なGitHubのリンクは提供されていない。しかし、以下のGo CLのリンクから関連するIssueを確認できる。
- Go Code Review (CL) 5779043: https://golang.org/cl/5779043
参考にした情報源リンク
- Go Code Review (CL) 5779043: https://golang.org/cl/5779043
- Go
testing
パッケージのドキュメント (一般的な情報): https://pkg.go.dev/testing - Go Exampleのドキュメント (一般的な情報): https://go.dev/blog/examples