[インデックス 19656] ファイルの概要
このコミットは、src/pkg/bufio/scan_test.go
ファイルに対する変更です。具体的には、テストコード内で使用されているエラー報告関数 t.Fatal
の呼び出しが t.Fatalf
に修正されています。これにより、エラーメッセージのフォーマットが正しく行われるようになります。
コミット
bufio: Fixed call to Fatal, should be Fatalf.
LGTM=iant
R=golang-codereviews, iant, bradfitz
CC=golang-codereviews
https://golang.org/cl/107390044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/22c3f67cd637d6d99bafc04867e7e4f8833f7f16
元コミット内容
bufio: Fixed call to Fatal, should be Fatalf.
LGTM=iant
R=golang-codereviews, iant, bradfitz
CC=golang-codereviews
https://golang.org/cl/107390044
変更の背景
このコミットの背景には、Go言語の標準ライブラリである bufio
パッケージのテストコードにおける、エラー報告の誤った使用がありました。Goのテストフレームワークである testing
パッケージには、テストの失敗を報告するためのいくつかの関数が提供されています。その中でも t.Fatal
と t.Fatalf
は、テストを失敗としてマークし、現在のテスト関数を即座に終了させる点で共通していますが、引数の扱いが異なります。
元のコードでは、t.Fatal("scan failed: %v", scanner.Err())
のように、フォーマット文字列とそれに続く引数を t.Fatal
に渡していました。しかし、t.Fatal
は fmt.Print
と同様に引数をそのまま出力するだけで、fmt.Printf
のようにフォーマット文字列を解釈して引数を埋め込む機能は持っていません。このため、期待されるエラーメッセージ(例: "scan failed: some error")ではなく、"scan failed: %v some error" のように、フォーマット指定子 %v
がそのまま表示されてしまうという問題がありました。
この問題を修正し、意図した通りにフォーマットされたエラーメッセージを出力するために、t.Fatalf
への変更が必要とされました。t.Fatalf
は fmt.Printf
と同様にフォーマット文字列と可変長引数を受け取り、それらを適切にフォーマットして出力します。
前提知識の解説
Go言語のtesting
パッケージ
Go言語には、ユニットテストを記述するための標準パッケージ testing
が用意されています。テスト関数は func TestXxx(t *testing.T)
の形式で定義され、*testing.T
型の引数 t
を通じてテストの制御や結果の報告を行います。
*testing.T
オブジェクトには、テストの失敗を報告するためのいくつかのメソッドがあります。
t.Error(args ...interface{})
: テストを失敗としてマークしますが、テスト関数の実行は継続します。引数はfmt.Print
と同様に扱われます。t.Errorf(format string, args ...interface{})
:t.Error
と同様にテストを失敗としてマークし、テスト関数の実行は継続します。引数はfmt.Printf
と同様にフォーマット文字列と可変長引数として扱われます。t.Fatal(args ...interface{})
: テストを失敗としてマークし、現在のテスト関数の実行を即座に終了させます。引数はfmt.Print
と同様に扱われます。t.Fatalf(format string, args ...interface{})
:t.Fatal
と同様にテストを失敗としてマークし、現在のテスト関数の実行を即座に終了させます。引数はfmt.Printf
と同様にフォーマット文字列と可変長引数として扱われます。
このコミットでは、t.Fatal
と t.Fatalf
の違いが重要になります。
bufio
パッケージのScanner
bufio
パッケージは、バッファリングされたI/O操作を提供します。その中でも bufio.Scanner
は、入力ストリーム(io.Reader
)からデータを読み込み、定義された区切り文字(例えば、行、単語など)に基づいてトークンに分割するための便利なインターフェースを提供します。
Scanner
の主なメソッドには以下のようなものがあります。
scanner.Scan() bool
: 次のトークンを読み込みます。読み込みが成功した場合はtrue
を、エラーが発生したか入力の終わりに達した場合はfalse
を返します。scanner.Text() string
:Scan
メソッドによって読み込まれた最新のトークンを文字列として返します。scanner.Err() error
:Scan
メソッドがfalse
を返した場合に、発生したエラーを返します。エラーがない場合はnil
を返します。scanner.MaxTokenSize(int)
: スキャンされるトークンの最大サイズを設定します。scanner.Split(bufio.SplitFunc)
: トークンを分割するための関数(SplitFunc
)を設定します。bufio.ScanWords
やbufio.ScanLines
など、いくつかの組み込みのSplitFunc
があります。
このコミットのテストコードでは、bufio.Scanner
を使用して単語の分割処理をテストしており、scanner.Err()
や scanner.Text()
の結果を検証しています。
技術的詳細
このコミットの技術的な核心は、Go言語の testing
パッケージにおける Fatal
と Fatalf
メソッドのセマンティクスの違いを正しく理解し、適用することにあります。
元のコードでは、以下のように記述されていました。
t.Fatal("scan failed: %v", scanner.Err())
t.Fatal("unexpected token: %v", token)
ここで t.Fatal
は、可変長引数を受け取りますが、それらを fmt.Print
のように扱います。つまり、引数をスペースで区切って連結し、改行を追加して出力します。したがって、"scan failed: %v"
という文字列と scanner.Err()
の値がそれぞれ独立した引数として扱われ、出力は例えば scan failed: %v some error
のようになります。これは、%v
がフォーマット指定子として機能することを期待している開発者の意図とは異なります。
一方、修正後のコードでは t.Fatalf
が使用されています。
t.Fatalf("scan failed: %v", scanner.Err())
t.Fatalf("unexpected token: %v", token)
t.Fatalf
は、最初の引数をフォーマット文字列として解釈し、それに続く引数をそのフォーマット文字列に埋め込みます。これは fmt.Printf
と同じ挙動です。したがって、"scan failed: %v"
はフォーマット文字列として機能し、scanner.Err()
の値が %v
の位置に適切に埋め込まれ、期待通りのエラーメッセージ(例: scan failed: some error
)が出力されます。
この変更は、単なる構文上の修正以上の意味を持ちます。テストが失敗した際に、より正確で読みやすいエラーメッセージが出力されるようになります。これにより、テストのデバッグが容易になり、問題の特定にかかる時間が短縮されます。特に、%v
のようなフォーマット指定子を使用している場合、Fatalf
を使用することは、開発者の意図を明確にし、テストの出力の品質を向上させる上で不可欠です。
コアとなるコードの変更箇所
変更は src/pkg/bufio/scan_test.go
ファイルの以下の部分です。
--- a/src/pkg/bufio/scan_test.go
+++ b/src/pkg/bufio/scan_test.go
@@ -413,9 +413,9 @@ func TestScanWordsExcessiveWhiteSpace(t *testing.T) {
scanner.MaxTokenSize(smallMaxTokenSize)
scanner.Split(ScanWords)
if !scanner.Scan() {
-\t\tt.Fatal(\"scan failed: %v\", scanner.Err())\n+\t\tt.Fatalf(\"scan failed: %v\", scanner.Err())\n \t}\n \tif token := scanner.Text(); token != word {\n-\t\tt.Fatal(\"unexpected token: %v\", token)\n+\t\tt.Fatalf(\"unexpected token: %v\", token)\n \t}\n }\
コアとなるコードの解説
このコミットでは、TestScanWordsExcessiveWhiteSpace
というテスト関数内の2箇所で、t.Fatal
の呼び出しが t.Fatalf
に変更されています。
-
if !scanner.Scan() { ... }
ブロック内:- 変更前:
t.Fatal("scan failed: %v", scanner.Err())
- 変更後:
t.Fatalf("scan failed: %v", scanner.Err())
- この部分は、
scanner.Scan()
がfalse
を返した場合、つまりスキャンに失敗した場合に実行されます。元のコードでは、scanner.Err()
から得られるエラー情報を%v
でフォーマットしようとしていましたが、t.Fatal
の挙動により、フォーマットが適用されず、%v
がそのまま出力されていました。t.Fatalf
に変更することで、scanner.Err()
の値が適切にフォーマットされ、より具体的なエラーメッセージがテスト出力に表示されるようになります。
- 変更前:
-
if token := scanner.Text(); token != word { ... }
ブロック内:- 変更前:
t.Fatal("unexpected token: %v", token)
- 変更後:
t.Fatalf("unexpected token: %v", token)
- この部分は、スキャンされたトークン (
scanner.Text()
) が期待される単語 (word
) と一致しなかった場合に実行されます。ここでも同様に、token
の値を%v
でフォーマットしようとしていましたが、t.Fatal
の挙動により意図通りにフォーマットされませんでした。t.Fatalf
に変更することで、token
の値が適切に埋め込まれ、「予期しないトークン」が何であったかを明確に伝えるエラーメッセージが出力されるようになります。
- 変更前:
これらの変更は、テストの失敗時に出力されるメッセージの品質を向上させ、デバッグプロセスをより効率的にするためのものです。
関連リンク
- Go Code Review: https://golang.org/cl/107390044
参考にした情報源リンク
- Go言語公式ドキュメント:
testing
パッケージ: https://pkg.go.dev/testing - Go言語公式ドキュメント:
bufio
パッケージ: https://pkg.go.dev/bufio - Go言語公式ドキュメント:
fmt
パッケージ: https://pkg.go.dev/fmt