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

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

このコミットは、Go言語の標準ライブラリにtestingパッケージの初期バージョンを導入するものです。具体的には、テストの定義と実行をサポートするための基本的なフレームワークを提供します。

コミット

commit 7969860126f51194929328418833344111e89467
Author: Rob Pike <r@golang.org>
Date:   Tue Nov 18 15:29:10 2008 -0800

    testing support library
    
    R=rsc
    OCL=19496
    CL=19496

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

https://github.com/golang/go/commit/7969860126f51194929328418833344111e89467

元コミット内容

testing support library

R=rsc
OCL=19496
CL=19496

変更の背景

このコミットは、Go言語がまだ初期開発段階にあった2008年11月に行われました。Go言語は、その設計思想の一つとして、並行処理の容易さやシンプルな構文を掲げていましたが、ソフトウェア開発において不可欠な要素である「テスト」に関する標準的な仕組みはまだ存在していませんでした。

このコミットの目的は、Go言語で書かれたプログラムの品質と信頼性を確保するための、基本的なテストサポートライブラリを提供することにあります。これにより、開発者は自身のコードが期待通りに動作するかを検証するためのテストを記述し、自動的に実行できるようになります。testingパッケージの導入は、Go言語エコシステムの健全な成長と、堅牢なアプリケーション開発を促進するための重要な一歩でした。

当時のGo言語は、まだオープンソースとして公開される前であり、Google社内で開発が進められていました。このコミットは、Go言語の設計者の一人であるRob Pike氏によって行われており、言語のコア機能の一部としてテストフレームワークが最初期から考慮されていたことを示しています。

前提知識の解説

1. Go言語の基本的な構造

Go言語は、Googleによって開発された静的型付けのコンパイル型言語です。パッケージ(package)という単位でコードを整理し、import文で他のパッケージの機能を利用します。関数はfuncキーワードで定義され、構造体(struct)は関連するデータをまとめるために使用されます。

2. テストフレームワークの役割

ソフトウェア開発におけるテストフレームワークは、コードの正確性を検証するためのツールセットです。主な役割は以下の通りです。

  • テストの記述: 特定の機能やコンポーネントが正しく動作するかを確認するテストケースを記述するための構造を提供します。
  • テストの実行: 記述されたテストケースを自動的に実行し、その結果(成功/失敗)を報告します。
  • 結果の報告: テストの実行結果を開発者が理解しやすい形式で表示します。失敗したテストがあれば、その詳細を報告し、問題の特定を助けます。
  • 品質保証: テストを継続的に実行することで、コードの変更が既存の機能に悪影響を与えていないか(回帰バグ)を確認し、ソフトウェアの品質を維持・向上させます。

3. exportキーワード(当時のGo言語)

このコミット時点のGo言語では、現在のGo言語とは異なり、外部に公開する型や関数にはexportキーワードを明示的に付けていました。現在のGo言語では、識別子(変数名、関数名、型名など)の最初の文字が大文字である場合に、その識別子がパッケージ外にエクスポート(公開)されるというルールになっています。このコミットのコードは、Go言語の初期の構文を示しており、現在のGo言語とは異なる点があることに注意が必要です。

4. sys.exit(1)

sys.exit(1)は、プログラムの実行を終了し、終了ステータスとして1を返すことを意味します。Unix系システムでは、終了ステータス0は成功を、0以外の値は通常、何らかのエラーや異常終了を示します。テストフレームワークにおいて、テストが一つでも失敗した場合にsys.exit(1)を呼び出すことで、CI/CDパイプラインやスクリプトがテストの失敗を検知し、ビルドプロセスを中断するなどの適切なアクションを取ることができます。

技術的詳細

このコミットで導入されたsrc/lib/testing.goファイルは、Go言語のtestingパッケージの基礎を築いています。その主要な構成要素は以下の通りです。

Test構造体

export type Test struct {
	name string;
	f *() bool;
}

Test構造体は、個々のテストケースを表現するために定義されています。

  • name string: テストの名前を保持する文字列フィールドです。テスト結果の出力時に、どのテストが成功/失敗したかを識別するために使用されます。
  • f *() bool: テストロジックをカプセル化する関数へのポインタです。この関数は引数を取らず、bool型の値を返します。テストが成功した場合はtrueを、失敗した場合はfalseを返すことを想定しています。これは、現在のGo言語のtesting.T型を引数にとるテスト関数(func TestXxx(t *testing.T))とは異なる、非常にシンプルな初期の設計です。

Main関数

export func Main(tests *[]Test) {
	ok := true;
	for i := 0; i < len(tests); i++ {
		ok1 := tests[i].f();
		status := "FAIL";
		if ok1 {
			status = "PASS"
		}
		ok = ok && ok1;
		println(status, tests[i].name);
	}
	if !ok {
		sys.exit(1);
	}
}

Main関数は、テストスイートのエントリーポイントとして機能します。

  • 引数として*[]Test、つまりTest構造体のスライスへのポインタを受け取ります。これにより、複数のテストケースをまとめて実行できます。
  • ok := true;で全体のテスト結果を追跡するフラグを初期化します。
  • forループを使用して、引数で渡されたtestsスライス内の各Test構造体を順番に処理します。
  • ok1 := tests[i].f();で、各テストケースに紐付けられた関数fを実行し、その結果(trueまたはfalse)をok1に格納します。
  • status := "FAIL"; if ok1 { status = "PASS" }により、ok1の値に基づいてテストのステータス文字列("PASS"または"FAIL")を決定します。
  • ok = ok && ok1;で、現在のテストの結果を全体のテスト結果に論理積で反映させます。これにより、一つでもテストが失敗すれば、okfalseになります。
  • println(status, tests[i].name);で、各テストのステータスとその名前を標準出力に出力します。これは、テストの進行状況と結果をユーザーに伝えるための基本的なメカニズムです。
  • ループの終了後、if !ok { sys.exit(1); }によって、もし一つでもテストが失敗していた場合(okfalseの場合)は、プログラムを終了コード1で終了させます。これは、テストが失敗したことを外部のシステム(例: ビルドシステム、CI/CDツール)に通知するための標準的な方法です。

この設計は、現在のGo言語のgo testコマンドやtestingパッケージの機能と比較すると非常にシンプルですが、テストフレームワークの核となる「テストの実行」「結果の集計」「ステータス報告」という機能を初期段階で実現しています。

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

このコミットでは、src/lib/testing.goという新しいファイルが追加されました。

--- /dev/null
+++ b/src/lib/testing.go
@@ -0,0 +1,26 @@
+// Copyright 2009 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
+
+export type Test struct {
+	name string;
+	f *() bool;
+}
+
+export func Main(tests *[]Test) {
+	ok := true;
+	for i := 0; i < len(tests); i++ {
+		ok1 := tests[i].f();
+		status := "FAIL";
+		if ok1 {
+			status = "PASS"
+		}
+		ok = ok && ok1;
+		println(status, tests[i].name);
+	}
+	if !ok {
+		sys.exit(1);
+	}
+}

コアとなるコードの解説

追加されたsrc/lib/testing.goファイルは、Go言語のテスト機能の根幹をなすtestingパッケージを定義しています。

  1. パッケージ宣言: package testingにより、このファイルがtestingパッケージの一部であることを示します。
  2. 著作権表示とライセンス: ファイルの冒頭には、Go言語の標準的な著作権表示とBSDスタイルのライセンスに関する記述があります。これは、Go言語のオープンソースプロジェクトにおける一般的な慣習です。
  3. Test構造体の定義: export type Test struct { name string; f *() bool; }により、テストケースの基本単位となるTest構造体が定義されています。nameはテストの識別子、fはテストロジックをカプセル化する関数ポインタです。この関数はテストの成否をブール値で返します。
  4. Main関数の定義: export func Main(tests *[]Test) { ... }は、テストランナーとしての役割を果たす関数です。
    • testsスライス(Test構造体のリスト)を受け取り、各テストをループで実行します。
    • 各テスト関数f()の実行結果に応じて、"PASS"または"FAIL"のステータスを決定し、テスト名と共に標準出力に表示します。
    • 全てのテストが完了した後、一つでも失敗したテストがあれば、sys.exit(1)を呼び出してプログラムを異常終了させます。これにより、自動化されたビルドシステムなどがテストの失敗を容易に検知できます。

このコードは、Go言語のテストフレームワークの非常に初期の、しかし機能的な基盤を確立しています。現在のGo言語のtestingパッケージは、この初期バージョンから大幅に進化し、並行テスト、ベンチマークテスト、カバレッジレポートなど、より高度な機能を提供していますが、そのルーツはこのシンプルなMain関数とTest構造体に見ることができます。

関連リンク

  • Go言語公式ウェブサイト: https://go.dev/
  • Go言語のtestingパッケージのドキュメント: https://pkg.go.dev/testing (現在のバージョン)
  • Go言語の歴史に関する情報 (例: Wikipedia, Go blogなど)

参考にした情報源リンク