[インデックス 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
関数の変更に焦点を当てています。
-
marshalErrorTests
構造体のフィールド名変更:ExpectErr
フィールドがErr
にリネームされました。ExpectKind
フィールドがKind
にリネームされました。 この変更により、テストケースの定義がより簡潔になり、意図が明確になります。
-
marshalErrorTests
の初期化の簡素化: 構造体のフィールド名変更に伴い、テストケースの初期化部分も新しいフィールド名に合わせて修正されています。これにより、テストデータの定義がより直接的になります。 -
TestMarshalErrors
関数のエラー検証ロジックの簡素化: 変更前は、エラーがnil
でないことのチェックと、エラー文字列の比較が別々のif
文で行われていました。 変更後は、if err == nil || err.String() != test.Err
という単一の条件で、エラーが存在し、かつ期待されるエラー文字列と一致するかどうかをチェックしています。これにより、エラー検証のロジックがより簡潔で読みやすくなりました。 同様に、UnsupportedTypeError
のKind
の検証も、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
}{
// ...
}
ExpectErr
とExpectKind
という冗長なフィールド名が、それぞれErr
とKind
という簡潔な名前に変更されました。これにより、テストケースの定義がより直接的で読みやすくなります。例えば、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)
}
この変更は、エラー検証ロジックを大幅に簡素化しています。
-
エラーの存在と文字列の検証: 変更前は、まずエラーが
nil
でないことを確認し、その後エラー文字列を比較していました。got, want := err, test.ExpectErr
というGoの多重代入とif
文の組み合わせは、簡潔ではあるものの、複数の条件を一度に評価する際には読みにくくなることがあります。 変更後は、if err == nil || err.String() != test.Err
という単一の条件で、エラーが存在しない場合、またはエラー文字列が期待値と異なる場合にエラーを報告するようにしました。これにより、ロジックがより直接的で理解しやすくなっています。 -
エラーの種類の検証: 変更前は、
got, want := err.(*UnsupportedTypeError).Type.Kind(), test.ExpectKind
という形で、エラーの種類(reflect.Kind
)を取得し、それを期待値と比較していました。 変更後は、if kind := err.(*UnsupportedTypeError).Type.Kind(); kind != test.Kind
という形で、kind
という新しい変数にエラーの種類を代入し、それを直接test.Kind
と比較しています。これにより、コードがより明確になり、一時変数のスコープも適切に限定されます。
これらの変更は、テストコードの冗長性を減らし、可読性と保守性を向上させることに貢献しています。機能的な振る舞いは変わらず、テストの意図がより明確に表現されるようになりました。
関連リンク
- Go言語の
encoding/xml
パッケージのドキュメント: https://pkg.go.dev/encoding/xml - Go言語の
reflect
パッケージのドキュメント: https://pkg.go.dev/reflect - Go言語のテストに関するドキュメント: https://go.dev/doc/code#testing
参考にした情報源リンク
- Go言語公式ドキュメント
- Go言語のソースコード
- GitHubのコミット履歴