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

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

このコミットは、Go言語の標準ライブラリである testing パッケージに関するドキュメントの追加と、それに付随する gotest (現在の go test) ユーティリティの利用方法に関する説明を目的としています。具体的には、src/lib/testing.go ファイルに25行の追加が行われ、テストの記述方法や testing.T 型の役割について解説が加えられました。

コミット

commit 681299a4448ca577893c2f3c663adf268dabf0eb
Author: Rob Pike <r@golang.org>
Date:   Thu Mar 5 17:50:36 2009 -0800

    document testing and incidentally gotest
    
    R=rsc
    DELTA=25  (25 added, 0 deleted, 0 changed)
    OCL=25798
    CL=25802

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

https://github.com/golang/go/commit/681299a4448ca577893c2f3c663adf268dabf0eb

元コミット内容

    document testing and incidentally gotest
    
    R=rsc
    DELTA=25  (25 added, 0 deleted, 0 changed)
    OCL=25798
    CL=25802

変更の背景

このコミットが行われた2009年3月は、Go言語がまだ一般に公開される前の初期開発段階にありました。Go言語の設計思想の一つに「シンプルさと実用性」があり、テストフレームワークもその例外ではありませんでした。この時期には、Go言語の基本的な機能が実装されつつあり、開発者がコードの品質を保証するためのテストの仕組みを整備する必要がありました。

testing パッケージは、Go言語に組み込まれたテストフレームワークであり、外部の依存関係なしにテストを記述・実行できることを目指していました。このコミットは、その testing パッケージの基本的な使い方、特にテスト関数の命名規則 (TestXxx) や testing.T 型の役割、そしてそれらを自動実行するための gotest (現在の go test コマンドの前身) ユーティリティについて、公式なドキュメントとしてコード内に記述することを目的としています。

初期のGo言語では、ドキュメントがコードコメントとして直接記述されることが多く、このコミットもその慣習に則っています。これにより、開発者はコードと同時にテストの書き方を理解できるようになり、Go言語におけるテスト文化の基礎が築かれました。

前提知識の解説

Go言語のテストの基本

Go言語には、標準ライブラリとして testing パッケージが提供されており、これを用いてユニットテスト、ベンチマークテスト、そして最近ではファズテストを記述することができます。Goのテストは非常にシンプルで、以下の特徴があります。

  • テストファイルの命名規則: テストファイルは、テスト対象のGoファイルと同じディレクトリに配置され、ファイル名の末尾に _test.go を付けます(例: my_package.go のテストは my_package_test.go)。
  • テスト関数の命名規則: テスト関数は Test で始まり、その後に続く文字列の最初の文字は大文字である必要があります(例: func TestMyFunction(t *testing.T))。この命名規則に従うことで、go test コマンドが自動的にテスト関数を認識し、実行します。
  • *testing.T: 各テスト関数は *testing.T 型の引数を一つ取ります。この t オブジェクトは、テストの失敗を報告したり、ログを出力したり、テストの実行を制御したりするためのメソッドを提供します。
    • t.Error() / t.Errorf(): テストを失敗としてマークしますが、テストの実行は継続します。
    • t.Fail(): テストを失敗としてマークしますが、テストの実行は継続します。
    • t.FailNow(): テストを失敗としてマークし、そのテスト関数の実行を即座に停止します。
    • t.Fatal() / t.Fatalf(): t.Log() / t.Logf() の後に t.FailNow() を呼び出すのと同等で、エラーメッセージをログに出力し、テストを失敗としてマークし、そのテスト関数の実行を即座に停止します。
    • t.Log() / t.Logf(): テスト中に情報をログに出力します。これはテストが失敗した場合や、go test -v オプションが指定された場合に表示されます。
    • t.Skip() / t.Skipf(): テストをスキップします。
    • t.Parallel(): テストを並行して実行できることを示します。
  • go test コマンド: Goのテストは、コマンドラインから go test コマンドを実行することで自動的に発見され、実行されます。このコマンドは、指定されたパッケージ内の _test.go ファイルを検索し、TestXxx 関数を実行します。

gotest ユーティリティ (現在の go test)

コミットメッセージにある gotest は、現在の go test コマンドの初期の名称です。Go言語のビルドシステムと密接に統合されており、テストの発見、コンパイル、実行、結果の報告までを一貫して行います。これにより、開発者はテストの実行環境を別途構築する必要がなく、Go言語のプロジェクトであればすぐにテストを開始できるという利点があります。

技術的詳細

このコミットは、src/lib/testing.go ファイルの冒頭に、testing パッケージの目的と gotest ユーティリティとの連携について説明するコメントを追加しています。

追加されたコメントは以下の点を明確にしています。

  1. testing パッケージの目的: Goパッケージの自動テストをサポートすること。
  2. gotest ユーティリティとの連携: testing パッケージは gotest ユーティリティと連携して使用されることを前提としていること。
  3. テスト関数の命名規則: func TestXxx(*testing.T) の形式の関数が gotest によって自動的に実行されること。ここで Xxx は任意の英数字の文字列であり、最初の文字は小文字であってはならないこと。
  4. テスト関数の配置場所: これらの TestXxx ルーチンは、テスト対象のパッケージ内に宣言されるべきであること。

さらに、testing.T 型の各メソッド(Fail, FailNow, Log, Logf, Error, Errorf, Fatal, Fatalf)にも、それぞれの機能と役割を説明するコメントが追加されています。これにより、testing.T オブジェクトがテストの状態管理、ログ記録、およびテスト結果の報告にどのように使用されるかが明確になります。

特に注目すべきは、FailNow() メソッドの説明に sys.Goexit() が含まれている点です。これは、FailNow() が呼び出された際に、現在のゴルーチン(テスト関数)の実行を即座に終了させることを示しています。これにより、テストが失敗した際に、それ以降のテストコードが実行されるのを防ぎ、テスト結果の信頼性を高めます。

また、Test 構造体と Main 関数にもコメントが追加されており、これらが gotest の実装の一部としてパッケージ間でエクスポートされている内部型および関数であることが説明されています。これは、gotesttesting パッケージの内部構造を利用してテストを実行していることを示唆しています。

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

--- a/src/lib/testing.go
+++ b/src/lib/testing.go
@@ -2,6 +2,13 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// The testing package provides support for automated testing of Go packages.
+// It is intended to be used in concert with the ``gotest\'\' utility, which automates
+// execution of any function of the form
+//     func TestXxx(*testing.T)
+// where Xxx can by any alphanumeric string (but the first letter must not be in
+// [a-z]) and serves to identify the test routine.
+// These TestXxx routines should be declared within the package they are testing.
 package testing
 
 import (
@@ -9,6 +16,7 @@ import (
 	"flag";
 )
 
+// Report as tests are run; default is silent for success.
 var chatty = flag.Bool("chatty", false, "chatty")
 
 // Insert tabs after newlines - but not the last one
@@ -21,26 +29,35 @@ func tabify(s string) string {
 	return s
 }
 
+// T is a type passed to Test functions to manage test state and support formatted test logs.
+// Logs are accumulated during execution and dumped to standard error when done.
 type T struct {
 	errors	string;
 	failed	bool;
 	ch	chan *T;
 }
 
+// Fail marks the Test function as having failed but continues execution.
 func (t *T) Fail() {
 	t.failed = true
 }
 
+// FailNow marks the Test function as having failed and stops its execution.
+// Execution will continue at the next Test.
 func (t *T) FailNow() {
 	t.Fail();
 	t.ch <- t;
 	sys.Goexit();
 }
 
+// Log formats its arguments using default formatting, analogous to Print(),
+// and records the text in the error log.
 func (t *T) Log(args ...) {
 	t.errors += "\t" + tabify(fmt.Sprintln(args));
 }
 
+// Log formats its arguments according to the format, analogous to Printf(),
+// and records the text in the error log.
 func (t *T) Logf(format string, args ...) {
 	t.errors += tabify(fmt.Sprintf("\t" + format, args));
 	l := len(t.errors);
@@ -49,26 +66,32 @@ func (t *T) Logf(format string, args ...) {
 	}
 }
 
+// Error is equivalent to Log() followed by Fail().
 func (t *T) Error(args ...) {
 	t.Log(args);
 	t.Fail();
 }
 
+// Errorf is equivalent to Logf() followed by Fail().
 func (t *T) Errorf(format string, args ...) {
 	t.Logf(format, args);
 	t.Fail();
 }
 
+// Fatal is equivalent to Log() followed by FailNow().
 func (t *T) Fatal(args ...) {
 	t.Log(args);
 	t.FailNow();
 }
 
+// Fatalf is equivalent to Logf() followed by FailNow().
 func (t *T) Fatalf(format string, args ...) {
 	t.Logf(format, args);
 	t.FailNow();
 }
 
+// An internal type but exported because it is cross-package; part of the implementation
+// of gotest.
 type Test struct {
 	Name string;
 	F func(*T);
@@ -79,6 +102,8 @@ func tRunner(t *T, test *Test) {
 	t.ch <- t;
 }
 
+// An internal function but exported because it is cross-package; part of the implementation
+// of gotest.
 func Main(tests []Test) {
 	flag.Parse();
 	ok := true;

コアとなるコードの解説

このコミットの主要な変更は、src/lib/testing.go ファイルへのコメントの追加です。

  1. パッケージレベルのコメント追加:

    // The testing package provides support for automated testing of Go packages.
    // It is intended to be used in concert with the ``gotest\'\' utility, which automates
    // execution of any function of the form
    //     func TestXxx(*testing.T)
    // where Xxx can by any alphanumeric string (but the first letter must not be in
    // [a-z]) and serves to identify the test routine.
    // These TestXxx routines should be declared within the package they are testing.
    

    このコメントは、testing パッケージの目的と、gotest (現在の go test) ユーティリティとの連携について説明しています。特に、TestXxx という命名規則に従うテスト関数が自動的に実行されること、そしてそれらの関数がテスト対象のパッケージ内に配置されるべきであることが明記されています。これは、Go言語におけるテストの基本的な規約を確立する上で非常に重要です。

  2. T 構造体へのコメント追加:

    // T is a type passed to Test functions to manage test state and support formatted test logs.
    // Logs are accumulated during execution and dumped to standard error when done.
    type T struct {
    	errors	string;
    	failed	bool;
    	ch	chan *T;
    }
    

    testing.T 型がテスト関数の引数として渡され、テストの状態管理とログ記録に使用されることが説明されています。ログは実行中に蓄積され、テスト完了時に標準エラー出力にダンプされるという動作も示されています。

  3. T 構造体のメソッドへのコメント追加: Fail(), FailNow(), Log(), Logf(), Error(), Errorf(), Fatal(), Fatalf() といった testing.T の各メソッドに、それぞれの機能と振る舞いを説明するコメントが追加されています。

    • Fail(): テストを失敗としてマークするが、実行は継続する。
    • FailNow(): テストを失敗としてマークし、そのテスト関数の実行を停止する。sys.Goexit() を呼び出すことで、現在のゴルーチンを終了させる。
    • Log() / Logf(): ログメッセージを記録する。
    • Error() / Errorf(): Log() / Logf() の後に Fail() を呼び出すのと同等。
    • Fatal() / Fatalf(): Log() / Logf() の後に FailNow() を呼び出すのと同等。
  4. Test 構造体と Main 関数へのコメント追加:

    // An internal type but exported because it is cross-package; part of the implementation
    // of gotest.
    type Test struct {
    	Name string;
    	F func(*T);
    }
    // An internal function but exported because it is cross-package; part of the implementation
    // of gotest.
    func Main(tests []Test) {
    	// ...
    }
    

    Test 構造体と Main 関数が、gotest の実装の一部としてパッケージ間でエクスポートされている内部的な型および関数であることが説明されています。これは、gotesttesting パッケージの内部APIを利用してテストの実行をオーケストレーションしていることを示しています。

これらのコメントは、Go言語のテストフレームワークの初期設計における意図と、その基本的な使用方法を明確にする上で非常に重要な役割を果たしています。

関連リンク

参考にした情報源リンク