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

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

このコミットは、Go言語のbignumパッケージのテストファイルであるbignum_test.goを、Goの標準テストフレームワークに準拠するようにリファクタリングするものです。具体的には、テストの実行方法を従来のシェルスクリプトベースの実行から、go testコマンドで実行可能な形式へと変更しています。

コミット

commit 0432a34383625a35f8f68c72d3a77e042566a08a
Author: Russ Cox <rsc@golang.org>
Date:   Mon Nov 24 12:32:31 2008 -0800

    make bignum_test a test
    
    R=gri
    DELTA=967  (468 added, 499 deleted, 0 changed)
    OCL=19906
    CL=19912

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

https://github.com/golang/go/commit/0432a34383625a35f8f68c72d3a77e042566a08a

元コミット内容

make bignum_test a test

このコミットメッセージは簡潔ですが、bignum_testというファイル(またはモジュール)を「テスト」として機能させるための変更であることを示唆しています。これは、単なるコードの変更ではなく、そのコードの役割や実行方法に関する根本的な変更を意味します。

変更の背景

このコミットが行われた2008年11月は、Go言語がまだ一般に公開される前の開発初期段階でした。Go言語の設計思想の一つに、シンプルで効率的なテストフレームワークの提供があります。初期のGoのテストは、シェルスクリプトを介して実行されることが一般的でしたが、これはGo言語の哲学とは必ずしも一致しませんでした。

このコミットの背景には、Go言語の標準ライブラリやアプリケーションのテストを、Go言語自体が提供するtestingパッケージとgo testコマンドによって統一的に管理・実行できるようにするという、Go言語開発チームの明確な意図があります。これにより、テストの記述、実行、結果の解析がよりGo言語のイディオムに沿った形になり、開発効率とコード品質の向上が期待されます。

bignum_test.goは、Go言語のbignum(多倍長整数)パッケージの機能検証を行うためのテストコードでした。このテストをGoの標準テストフレームワークに統合することで、Goエコシステム全体でのテストの一貫性と保守性が向上します。

前提知識の解説

Go言語のtestingパッケージ

Go言語には、標準ライブラリとしてtestingパッケージが提供されています。このパッケージは、ユニットテスト、ベンチマークテスト、例(Example)テストなどを記述するための基本的な機能を提供します。

  • テスト関数の命名規則: testingパッケージを使用するテスト関数は、Testで始まり、その後に続く文字列の最初の文字が大文字である必要があります(例: func TestSomething(t *testing.T))。
  • *testing.T: テスト関数は、*testing.T型の引数を一つ取ります。このtオブジェクトを通じて、テストの失敗を報告したり、ログを出力したり、テストのヘルパー関数を呼び出したりします。
  • テストの実行: Goのテストは、プロジェクトのルートディレクトリまたはテストファイルが存在するディレクトリでgo testコマンドを実行することで自動的に発見され、実行されます。
  • アサーション: Goのtestingパッケージには、JUnitやNUnitのような明示的なアサーションライブラリは含まれていません。代わりに、t.Error(), t.Errorf(), t.Fatal(), t.Fatalf()などのメソッドを使用して、条件が満たされない場合にテストの失敗を報告します。t.Fatal()t.Fatalf()は、テストの失敗を報告した後に現在のテスト関数の実行を停止します。

Go言語のパッケージとインポート

Go言語のコードはパッケージにまとめられます。

  • package main: 実行可能なプログラムのエントリポイントとなるパッケージです。main関数を含みます。
  • package <name>: ライブラリとして機能するパッケージです。他のパッケージからインポートして利用されます。
  • インポート: importキーワードを使用して、他のパッケージの機能を利用します。import "fmt"のように直接パッケージ名を指定することもできますし、import Fmt "fmt"のようにエイリアスを付けてインポートすることも可能です。

panic()とエラーハンドリング

Go言語では、エラーハンドリングにerrorインターフェースと多値戻り値を使用することが推奨されています。panic()は、回復不可能なエラーやプログラマーの論理的な誤りを示すために使用されることが多く、通常はプログラムの実行を停止させます。テストにおいては、panic()を使用するとテストランナーが予期せぬ終了をする可能性があり、テスト結果の報告が適切に行われないことがあります。testingパッケージのt.Fatalf()などは、テストの失敗を適切に報告し、テストランナーが次のテストに進むことを可能にします。

技術的詳細

このコミットの技術的な変更点は、bignum_test.goファイルをGoの標準テストフレームワークに適合させるためのものです。

  1. ファイルパスの変更:

    • test/bignum_test.goからsrc/lib/bignum_test.goへのリネーム。これは、Goのソースコード管理におけるテストファイルの配置規則の変更を示唆しています。初期のGoではテストファイルがtest/ディレクトリに置かれることがありましたが、後にテスト対象のパッケージと同じディレクトリに_test.goサフィックスを付けて配置する慣習が確立されました。この変更は、その慣習への移行の初期段階を示している可能性があります。
  2. パッケージ宣言の変更:

    • package mainからpackage bignum_testへの変更。
      • 元のpackage mainは、このファイルが単独で実行可能なプログラムであることを意味していました。テストは、main関数内で各テスト関数を直接呼び出す形式でした。
      • package bignum_testは、このファイルがbignumパッケージのテストコードであり、bignumパッケージとは別のテスト専用パッケージとしてコンパイルされることを意味します。これにより、テストコードがテスト対象のパッケージの内部(非エクスポート)要素にアクセスできるようになります。
  3. インポートの変更:

    • import Big "bignum"からimport bignum "bignum"へ、およびimport Fmt "fmt"からimport fmt "fmt"への変更。
      • これは、Goの慣習として、インポートするパッケージに明示的なエイリアスを付けない(または、パッケージ名と同じエイリアスを付ける)ことを推奨するスタイルへの移行を示しています。これにより、コードの可読性が向上し、どのパッケージの関数が呼び出されているかが明確になります。
    • import testing "testing"の追加。
      • これは、Goの標準テストフレームワークを利用するために必須の変更です。
  4. テスト実行スクリプトの削除:

    • ファイルの先頭にあった// $G $D/$F.go && $L $F.$A && ./$A.outというコメント行が削除されました。これは、このファイルがもはやシェルスクリプトによって直接コンパイル・実行されるのではなく、go testコマンドによって処理されることを明確に示しています。
  5. テストヘルパー関数の変更:

    • TEST, NAT_EQ, INT_EQ, RAT_EQといったカスタムアサーション関数が変更されました。
      • これらの関数は、引数として*testing.T型のtesterを受け取るようになりました。
      • エラー報告の方法がprintlnpanic()からtester.Fatalf()に変更されました。
        • printlnは標準出力にメッセージを出すだけでテストの失敗をGoのテストランナーに伝えません。
        • panic()はプログラム全体を停止させる可能性があり、テストスイート全体の実行を妨げます。
        • tester.Fatalf()は、テストの失敗をtestingパッケージに報告し、現在のテスト関数の実行を停止しますが、テストスイート全体の実行は継続させます。これにより、すべてのテストが実行され、結果が適切に集計されます。
      • tester *testing.Tというグローバル変数が導入され、各テスト関数内でtester = tと設定することで、ヘルパー関数が*testing.Tインスタンスにアクセスできるようにしています。これは、Goの初期のテストコードでよく見られたパターンですが、現代のGoでは通常、*testing.Tを直接ヘルパー関数に渡すか、クロージャを利用します。
  6. テスト関数の命名とシグネチャの変更:

    • NatConv(), IntConv(), RatConv(), NatAdd(), NatSub(), NatMul(), NatDiv(), IntQuoRem(), IntDivMod(), NatMod(), NatShift(), IntShift(), NatCmp(), NatLog2(), NatGcd(), NatPow(), NatPop()といった関数が、それぞれexport func TestNatConv(t *testing.T), export func TestIntConv(t *testing.T)のように変更されました。
      • exportキーワードは、Goの初期の構文で、関数がエクスポートされることを示していました(現在のGoでは、関数名の最初の文字を大文字にすることでエクスポートされます)。
      • 関数名がTestで始まり、*testing.T型の引数を取ることで、go testコマンドがこれらの関数を自動的にテストとして認識し、実行するようになります。
  7. main関数の削除:

    • 以前はmain関数がすべてのテスト関数を呼び出していましたが、go testコマンドがテスト関数を自動的に発見・実行するため、このmain関数は不要となり削除されました。

これらの変更は、Go言語のテストフレームワークの設計思想と実装が固まりつつあった時期の重要な移行を示しています。

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

このコミットのコアとなる変更は、bignum_test.goファイル全体のリファクタリングです。特に以下の点が重要です。

  1. パッケージ宣言の変更:

    --- a/test/bignum_test.go
    +++ b/src/lib/bignum_test.go
    @@ -2,12 +2,13 @@
     // Use of this source code is governed by a BSD-style
     // license that can be found in the LICENSE file.
     
    -// $G $D/$F.go && $L $F.$A && ./$A.out
    +package bignum_test
     
    -package main
    -
    -import Big "bignum"
    -import Fmt "fmt"
    +import (
    +	bignum "bignum";
    +	fmt "fmt";
    +	testing "testing";
    +)
    
    • package mainからpackage bignum_testへの変更。
    • testingパッケージのインポート。
    • bignumfmtパッケージのエイリアスを削除または変更。
  2. テストヘルパー関数のエラー報告の変更:

    --- a/test/bignum_test.go
    +++ b/src/lib/bignum_test.go
    @@ -18,92 +19,83 @@ const (
      	sp = "170141183460469231731687303715884105727";  // prime
      )
      
    -
    -func NatFromString(s string, base uint, slen *int) *Big.Natural {
    -	x, dummy := Big.NatFromString(s, base, slen);
    +var tester *testing.T;
    +func TEST(n uint, b bool) {
    +	if !b {
    +		tester.Fatalf("TEST failed: %s (%d)", test_msg, n);
    +	}
    +}
    
    -func IntFromString(s string, base uint, slen *int) *Big.Integer {
    -	x, dummy := Big.IntFromString(s, base, slen);
    +func NAT_EQ(n uint, x, y *bignum.Natural) {
    +	if x.Cmp(y) != 0 {
    +		tester.Fatalf("TEST failed: %s (%d)\nx = %v\ny = %v", test_msg, n, x, y);
    +	}
    +}
    
    -func RatFromString(s string, base uint, slen *int) *Big.Rational {
    -	x, dummy := Big.RatFromString(s, base, slen);
    +func INT_EQ(n uint, x, y *bignum.Integer) {
    +	if x.Cmp(y) != 0 {
    +		tester.Fatalf("TEST failed: %s (%d)\nx = %v\ny = %v", test_msg, n, x, y);
    +	}
    +}
    
    -var (
    -	nat_zero = Big.Nat(0);
    -	nat_one = Big.Nat(1);
    -	nat_two = Big.Nat(2);
    +func RAT_EQ(n uint, x, y *bignum.Rational) {
    +	if x.Cmp(y) != 0 {
    +		tester.Fatalf("TEST failed: %s (%d)\nx = %v\ny = %v", test_msg, n, x, y);
    +	}
    +}
    
    • tester *testing.Tグローバル変数の導入。
    • printlnpanic()の呼び出しをtester.Fatalf()に置き換え。
  3. 各テスト関数のシグネチャと命名の変更、およびmain関数の削除:

    --- a/test/bignum_test.go
    +++ b/src/lib/bignum_test.go
    @@ -92,43 +103,45 @@ func NatConv() {
      }
      
     
    -func IntConv() {
    +export func TestIntConv(t *testing.T) {
    +	tester = t;
      	test_msg = "IntConv";
      	var slen int;
      	INT_EQ(0, IntFromString("0", 0, nil), int_zero);
    ...
    @@ -467,30 +467,3 @@ func NatPop() {
      	}
      }
      
    -
    -func main() {
    -	// Naturals
    -	NatConv();
    -	NatAdd();
    -	NatSub();
    -	NatMul();
    -	NatDiv();
    -	NatMod();
    -	NatShift();
    -	NatCmp();
    -	NatLog2();
    -	NatGcd();
    -	NatPow();
    -	NatPop();
    -
    -	// Integers
    -	// TODO add more tests
    -	IntConv();
    -	IntQuoRem();
    -	IntDivMod();
    -	IntShift();
    -
    -	// Rationals
    -	// TODO add more tests
    -	RatConv();
    -}
    
    • 各テスト関数がexport func Test<Name>(t *testing.T)の形式に変更され、tester = tが追加。
    • ファイルの末尾にあったmain関数が完全に削除。

コアとなるコードの解説

このコミットは、Go言語のテスト文化の初期段階における重要なマイルストーンを示しています。

  • Goのテストフレームワークへの移行: 以前は、テストコードがmainパッケージの一部として書かれ、main関数から直接呼び出されるか、外部のシェルスクリプトによって実行されていました。このコミットにより、bignum_test.goはGoの標準testingパッケージに完全に準拠するようになりました。これにより、go testコマンド一つでテストが自動的に発見・実行され、結果が統一された形式で報告されるようになります。

  • テストの独立性と並行性: testingパッケージを使用することで、各テスト関数は独立して実行されることが保証されます。また、go testはデフォルトでテストを並行して実行しようとします(ただし、このコミット時点では並行実行の明示的な設定は含まれていません)。これにより、大規模なテストスイートの実行時間が短縮され、開発サイクルが加速されます。

  • エラー報告の改善: panic()printlnから*testing.Tのメソッド(特にFatalf)への移行は、テストの堅牢性と報告の正確性を大幅に向上させます。Fatalfは、テストが失敗したことをテストランナーに明確に伝え、そのテスト関数の実行を停止しつつも、他のテストの実行を妨げません。これにより、テストスイート全体の結果をより信頼性高く把握できるようになります。

  • Goのイディオムへの準拠: パッケージ名の変更(mainからbignum_test)、インポートのエイリアス使用の抑制、テスト関数の命名規則の採用など、この変更はGo言語のコーディング規約とベストプラクティスへの準拠を促進します。これは、Go言語のコードベース全体の品質と一貫性を高める上で不可欠なステップでした。

このコミットは、単なるコードの修正ではなく、Go言語のテストエコシステムがどのように進化し、成熟していったかを示す歴史的な証拠とも言えます。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント: https://go.dev/doc/
  • Go言語のテストに関する公式ブログ記事やチュートリアル (Go言語の歴史的文脈を理解するために、初期の資料も参照しました)
    • "How to Write Go Code" (初期のGoのセットアップとテストに関する情報が含まれている可能性があります): https://go.dev/doc/code
    • "The Go Programming Language" (書籍、Goの設計思想とテストに関する章): https://www.gopl.io/
  • GitHubのコミット履歴とdiffビューア。