20

Goパッケージを初めて単体テストしようとしていますが、同じファイルにいくつかのエラーがあります。

type FooErr int
type BarErr int

func (e *FooErr) Error () string {
    return "A Foo Error has occurred"
}

func (e *BarErr) Error () string {
    return "A Bar Error has occurred"
}

ただし、すべての命名規則は次のように見えますfunc TestXxx(*testing.T)パッケージのドキュメントからtesting)。これは、私のテストファイルが次のようになることを意味します。

func TestError (t *testing.T) { ... } // FooErr
func TestError (t *testing.T) { ... } // BarErr

これは明らかに同じ署名の2つの機能です。これを処理するための推奨される方法は何ですか?

4

4 に答える 4

38

ここで考慮すべきことがいくつかあります。

エラー

パッケージレベルでエクスポートされたエラー値は、通常、名前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、ユニットとテストしている動作の両方を含むような説明的な名前を付けます。

于 2013-03-02T02:39:25.090 に答える
9

テストパッケージの概要セクションに記載されている関数の例については、次の規則に従います。

「関数F、タイプT、およびタイプTのメソッドMの例を宣言するための命名規則は次のとおりです。」

func ExampleF() { ... }
func ExampleT() { ... }
func ExampleT_M() { ... }

godocには、関数の例の命名規則が必要ですが、一貫性を保つために、テストの同じ規則であるTestT_Mに従います。

于 2013-03-01T05:49:15.860 に答える
5

TestXxxXxx部分を実際の関数名と一致させる必要はありません。コマンドがテストを取得するには、テストの前に接頭辞を付けるという規則でTest十分です。go test

Alex Lockwoodがコメントで述べているように、必要に応じてTestFooErrorとTestBarErrorを使用できます。

于 2013-03-01T00:50:26.123 に答える
2

Go 1.4(2014年第4四半期)では、テストメソッドの命名規則がもう1つ追加されます。

テストパッケージには、一連のテストの実行をより細かく制御できる新しい機能があります。
テストコードに関数が含まれている場合:

func TestMain(m *testing.M) 

テストを直接実行する代わりに、その関数が呼び出されます。構造体には、テストにアクセスして実行するためのメソッドが含まれています
M

于 2014-10-30T08:36:27.667 に答える