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

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

このコミットは、Go言語の標準ライブラリであるencoding/xmlパッケージ内のテストコードを簡素化することを目的としています。具体的には、XMLマーシャリング(Goのデータ構造をXML形式に変換する処理)におけるエラーテストの構造と検証ロジックが改善されています。

コミット

commit 29fb5d3e0f3ac82a3f6f9ac97e0b74f4dbf3b5f6
Author: Russ Cox <rsc@golang.org>
Date:   Thu Oct 27 19:40:41 2011 -0700

    xml: simplify test
    
    R=golang-dev, adg
    CC=golang-dev
    https://golang.org/cl/5320051

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

https://github.com/golang/go/commit/29fb5d3e0f3ac82a3f6f9ac97e0b74f4dbf3b5f6

元コミット内容

xml: simplify test

このコミットは、encoding/xmlパッケージのテストコードを簡素化することを目的としています。

変更の背景

Go言語のencoding/xmlパッケージは、Goのデータ構造とXMLの間で変換を行うための機能を提供します。このパッケージには、Goの特定の型(例えばチャネルやマップ)がXMLにマーシャリングできない場合にエラーを発生させるメカニズムがあります。

このコミットが行われた背景には、テストコードの可読性と保守性の向上が挙げられます。既存のテストコードでは、エラーの期待値と種類を保持する構造体のフィールド名が冗長であり、エラー検証ロジックもやや複雑でした。このコミットは、これらの冗長性を排除し、より簡潔で理解しやすいテストコードにすることを意図しています。特に、テストケースの定義とエラー検証の条件をより直接的にすることで、将来的なテストの追加や変更が容易になります。

前提知識の解説

Go言語のencoding/xmlパッケージ

encoding/xmlパッケージは、Goの構造体とXMLドキュメントの間でデータをエンコード(マーシャリング)およびデコード(アンマーシャリング)するための機能を提供します。Goの構造体のフィールドにタグを付与することで、XML要素名や属性名を制御できます。

Go言語のreflectパッケージとreflect.Kind

reflectパッケージは、Goプログラムが実行時に自身の構造を検査(リフレクション)することを可能にします。これにより、変数の型、値、メソッドなどを動的に調べることができます。

reflect.Kindは、Goの型の基本的なカテゴリ(例: Int, String, Struct, Chan, Mapなど)を表す列挙型です。reflect.TypeOf(v).Kind()のように使用することで、任意の変数の基本的な型カテゴリを取得できます。encoding/xmlパッケージでは、マーシャリングできない型を検出するためにreflect.Kindが利用されます。例えば、チャネルやマップはXMLに直接マーシャリングできないため、これらのKindをチェックしてエラーを発生させます。

Go言語のテスト

Go言語には、標準でテストフレームワークが組み込まれています。_test.goで終わるファイルにテストコードを記述し、go testコマンドで実行します。テスト関数はTestで始まり、*testing.T型の引数を取ります。エラーを報告するにはt.Errorf()などを使用します。

技術的詳細

このコミットは、src/pkg/xml/marshal_test.goファイル内のmarshalErrorTests構造体とTestMarshalErrors関数の変更に焦点を当てています。

  1. marshalErrorTests構造体のフィールド名変更:

    • ExpectErrフィールドがErrにリネームされました。
    • ExpectKindフィールドがKindにリネームされました。 この変更により、テストケースの定義がより簡潔になり、意図が明確になります。
  2. marshalErrorTestsの初期化の簡素化: 構造体のフィールド名変更に伴い、テストケースの初期化部分も新しいフィールド名に合わせて修正されています。これにより、テストデータの定義がより直接的になります。

  3. TestMarshalErrors関数のエラー検証ロジックの簡素化: 変更前は、エラーがnilでないことのチェックと、エラー文字列の比較が別々のif文で行われていました。 変更後は、if err == nil || err.String() != test.Errという単一の条件で、エラーが存在し、かつ期待されるエラー文字列と一致するかどうかをチェックしています。これにより、エラー検証のロジックがより簡潔で読みやすくなりました。 同様に、UnsupportedTypeErrorKindの検証も、if kind := err.(*UnsupportedTypeError).Type.Kind(); kind != test.Kindという形で、より直接的に比較するようになりました。

これらの変更は、機能的な変更ではなく、既存のテストコードの品質と保守性を向上させるためのリファクタリングです。

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

src/pkg/xml/marshal_test.go

--- a/src/pkg/xml/marshal_test.go
+++ b/src/pkg/xml/marshal_test.go
@@ -314,27 +314,27 @@ func TestMarshal(t *testing.T) {
 }
 
 var marshalErrorTests = []struct {
-	Value      interface{}
-	ExpectErr  string
-	ExpectKind reflect.Kind
+	Value interface{}
+	Err   string
+	Kind  reflect.Kind
 }{
 	{
-		Value:      make(chan bool),
-		ExpectErr:  "xml: unsupported type: chan bool",
-		ExpectKind: reflect.Chan,
+		Value: make(chan bool),
+		Err:   "xml: unsupported type: chan bool",
+		Kind:  reflect.Chan,
 	},
 	{
 		Value: map[string]string{
 			"question": "What do you get when you multiply six by nine?",
 			"answer":   "42",
 		},
-		ExpectErr:  "xml: unsupported type: map[string] string",
-		ExpectKind: reflect.Map,
+		Err:  "xml: unsupported type: map[string] string",
+		Kind: reflect.Map,
 	},
 	{
-		Value:      map[*Ship]bool{nil: false},
-		ExpectErr:  "xml: unsupported type: map[*xml.Ship] bool",
-		ExpectKind: reflect.Map,
+		Value: map[*Ship]bool{nil: false},
+		Err:   "xml: unsupported type: map[*xml.Ship] bool",
+		Kind:  reflect.Map,
 	},
 }
 
@@ -342,14 +342,11 @@ func TestMarshalErrors(t *testing.T) {
 	for idx, test := range marshalErrorTests {
 		buf := bytes.NewBuffer(nil)
 		err := Marshal(buf, test.Value)
-		if got, want := err, test.ExpectErr; got == nil {
-			t.Errorf("#%d: want error %s", idx, want)
-			continue
-		} else if got.String() != want {
-			t.Errorf("#%d: marshal(%#v) = [error] %q, want %q", idx, test.Value, got, want)
+		if err == nil || err.String() != test.Err {
+			t.Errorf("#%d: marshal(%#v) = [error] %q, want %q", idx, test.Value, err, test.Err)
 		}
-		if got, want := err.(*UnsupportedTypeError).Type.Kind(), test.ExpectKind; got != want {
-			t.Errorf("#%d: marshal(%#v) = [error kind] %s, want %s", idx, test.Value, got, want)
+		if kind := err.(*UnsupportedTypeError).Type.Kind(); kind != test.Kind {
+			t.Errorf("#%d: marshal(%#v) = [error kind] %s, want %s", idx, test.Value, kind, test.Kind)
 		}
 	}
 }

コアとなるコードの解説

marshalErrorTests構造体の変更

変更前:

var marshalErrorTests = []struct {
	Value      interface{}
	ExpectErr  string
	ExpectKind reflect.Kind
}{
	// ...
}

変更後:

var marshalErrorTests = []struct {
	Value interface{}
	Err   string
	Kind  reflect.Kind
}{
	// ...
}

ExpectErrExpectKindという冗長なフィールド名が、それぞれErrKindという簡潔な名前に変更されました。これにより、テストケースの定義がより直接的で読みやすくなります。例えば、ExpectErr: "..."と書く代わりにErr: "..."と書くことで、期待されるエラー文字列を直接指定していることが一目でわかります。

TestMarshalErrors関数の変更

変更前:

		if got, want := err, test.ExpectErr; got == nil {
			t.Errorf("#%d: want error %s", idx, want)
			continue
		} else if got.String() != want {
			t.Errorf("#%d: marshal(%#v) = [error] %q, want %q", idx, test.Value, got, want)
		}
		if got, want := err.(*UnsupportedTypeError).Type.Kind(), test.ExpectKind; got != want {
			t.Errorf("#%d: marshal(%#v) = [error kind] %s, want %s", idx, test.Value, got, want)
		}

変更後:

		if err == nil || err.String() != test.Err {
			t.Errorf("#%d: marshal(%#v) = [error] %q, want %q", idx, test.Value, err, test.Err)
		}
		if kind := err.(*UnsupportedTypeError).Type.Kind(); kind != test.Kind {
			t.Errorf("#%d: marshal(%#v) = [error kind] %s, want %s", idx, test.Value, kind, test.Kind)
		}

この変更は、エラー検証ロジックを大幅に簡素化しています。

  1. エラーの存在と文字列の検証: 変更前は、まずエラーがnilでないことを確認し、その後エラー文字列を比較していました。got, want := err, test.ExpectErrというGoの多重代入とif文の組み合わせは、簡潔ではあるものの、複数の条件を一度に評価する際には読みにくくなることがあります。 変更後は、if err == nil || err.String() != test.Errという単一の条件で、エラーが存在しない場合、またはエラー文字列が期待値と異なる場合にエラーを報告するようにしました。これにより、ロジックがより直接的で理解しやすくなっています。

  2. エラーの種類の検証: 変更前は、got, want := err.(*UnsupportedTypeError).Type.Kind(), test.ExpectKindという形で、エラーの種類(reflect.Kind)を取得し、それを期待値と比較していました。 変更後は、if kind := err.(*UnsupportedTypeError).Type.Kind(); kind != test.Kindという形で、kindという新しい変数にエラーの種類を代入し、それを直接test.Kindと比較しています。これにより、コードがより明確になり、一時変数のスコープも適切に限定されます。

これらの変更は、テストコードの冗長性を減らし、可読性と保守性を向上させることに貢献しています。機能的な振る舞いは変わらず、テストの意図がより明確に表現されるようになりました。

関連リンク

参考にした情報源リンク

  • Go言語公式ドキュメント
  • Go言語のソースコード
  • GitHubのコミット履歴