测试Golang中的命名约定

时间:2013-03-01 00:29:48

标签: go

我是第一次尝试单独测试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)from the testing package documentation)。这意味着我的测试文件看起来像这样:

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

这显然是同一签名的两个功能。处理此问题的推荐方法是什么?

4 个答案:

答案 0 :(得分:30)

这里有几点需要考虑:

错误

包级别导出的错误值通常命名为Err,后跟某些内容,例如ErrTimeout here。这样做是为了使您的包的客户可以执行类似

的操作
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] }

后一种方法的一个优点是它无法与任何其他包中的类型进行比较。

如果您在错误中需要一些额外数据,则该类型的名称以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不是经常出现的名称。但是,测试本身的名称必须是唯一的,并且不受约束以与示例相同的方式匹配被测代码中的任何内容。当您测试一段代码的多个条件时,通常最好将测试表示为Table Driven Test。该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之类的描述,包括单元和行为你正在测试。

答案 1 :(得分:6)

我会遵循测试包的overview部分中记录的示例函数的约定:

“为类型T声明函数F,类型T和方法M的示例的命名约定是:”

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

godoc需要示例函数的命名约定,但为了保持一致性,我会遵循相同的测试惯例TestT_M。

答案 2 :(得分:5)

您不需要让TestXxx的Xxx部分与实际的函数名称匹配。使用Test为测试添加前缀的约定足以让go test命令获取它们。

就像Alex Lockwood在评论中所说,如果你愿意,你可以使用TestFooError和TestBarError。

答案 3 :(得分:2)

Go 1.4 (Q4 2014)将为测试方法添加一个命名约定:

  

测试包有一个新工具,可以更好地控制运行一组测试   如果测试代码包含函数:

func TestMain(m *testing.M) 
  

将调用该函数,而不是直接运行测试   M struct包含访问和运行测试的方法。