ここで考慮すべきことがいくつかあります。
エラー
パッケージレベルでエクスポートされたエラー値は、通常、名前Err
の後に何かが続きます。たとえば、ErrTimeout
ここにあります。これは、パッケージのクライアントが次のようなことを実行できるようにするために行われます。
if err := yourpkg.Function(); err == yourpkg.ErrTimeout {
// timeout
} else if err != nil {
// some other error
}
これを容易にするために、それらは多くの場合、次のいずれかで作成されますerrors.New
。
// Error constants
var (
ErrTimeout = errors.New("yourpkg: connect timeout")
ErrInvalid = errors.New("yourpkg: invalid configuration")
)
または、カスタムのエクスポートされていないタイプの場合:
type yourpkgError int
// Error constants
var (
ErrTimeout yourpkgError = iota
ErrSyntax
ErrConfig
ErrInvalid
)
var errText = map[yourpkgError]string{
ErrTimeout: "yourpkg: connect timed out",
...
}
func (e yourpkgError) Error() string { return errText[e] }
後者のアプローチの利点の1つは、他のパッケージのタイプと同等に比較できないことです。
エラー内に追加のデータが必要な場合、タイプの名前は次のように終わりますError
。
type SyntaxError struct {
File string
Line, Position int
Description string
}
func (e *SyntaxError) Error() string {
return fmt.Sprintf("%s:%d:%d: %s", e.File, e.Line, e.Position, e.Description)
}
これは、前の等価性チェックとは対照的に、型アサーションを必要とします。
tree, err := yourpkg.Parse(file)
if serr, ok := err.(*SyntaxError); ok {
// syntax error
} else if err != nil {
// other error
}
いずれの場合も、パッケージのユーザーがいつ使用され、どの関数がコードを返すかを理解できるように、コードを文書化することが重要です。
テスト
多くの場合、テストは、テストしているユニットにちなんで名付けられます。多くの場合、エラー状態を個別にテストすることTestError
はないため、頻繁に表示される名前ではありません。ただし、テスト自体の名前は一意である必要があり、例と同じように、テスト対象のコード内の何かと一致するように制約されることはありません。コードの一部の複数の条件をテストする場合、多くの場合、テストをテーブル駆動テストとして定式化するのが最適です。そのwikiページにはいくつかの良い例がありますが、エラーチェックを示すために、次のようにすることができます。
func TestParse(t *testing.T) {
tests := []struct{
contents string
err error
}{
{"1st", nil},
{"2nd", nil},
{"third", nil},
{"blah", ErrBadOrdinal},
{"", ErrUnexpectedEOF},
}
for _, test := range tests {
file := strings.NewReader(test.contents)
if err := Parse(file); err != test.err {
t.Errorf("Parse(%q) error %q, want error %q", test.contents, err, test.err)
}
// other stuff
}
}
奇妙なことをしてメインテストに適合しないユニットに特別なテスト関数が必要な場合は、通常TestParseTimeout
、ユニットとテストしている動作の両方を含むような説明的な名前を付けます。