如何对Go语言的错误进行单元测试

55

当你在单元测试带有 error 返回类型的函数时,我想知道如何正确地对此错误进行单元测试。你应该只检查错误是否为 nil 还是非 nil?或者你应该验证错误字符串与期望字符串匹配吗?


1
可能是如何比较Golang错误对象的重复问题。 - 030
4
我不认为这是重复的@030,因为另一个问题只是关于比较错误。而这个问题是关于如何在单元测试中有效地比较错误。这些并不完全相同,并且我对它们的答案也会有所不同。 - Martin Tournoij
4个回答

45

在大多数情况下,您只需检查错误是否为nil。

我建议仅在绝对必要时检查错误字符串。一般来说,我认为错误字符串仅供人类阅读。

如果您需要更多关于错误的详细信息,一个更好的替代方案是使用自定义错误类型。然后,您可以在err.(type)上进行switch并查看它是否是您期望的类型。 如果您需要更多详细信息,您可以使自定义错误类型包含值,然后在测试中进行检查。

Go的错误只是具有Error() string方法的类型的接口,因此自己实现它们很简单。 https://blog.golang.org/error-handling-and-go


1
我确实喜欢使用自定义错误类型的想法,这肯定会简化事情。 - GabeMeister
8
我知道这有点过时了,但我认为这不应该成为被接受的答案。在测试中比较错误字符串对我很重要,因为它们就像其他值一样。编写单元测试时,请检查返回的错误是否符合您的预期,并检查是否已应用正确的错误包装。自定义错误类型会与调用者产生强烈耦合,通常这不是您想要的。 https://dave.cheney.net/2016/04/27/dont-just-check-errors-handle-them-gracefully - Michael Mallett
很多情况下,错误类型并不是绝对保证这是相同的错误。 - Al Amin

27

这里有一个很实用的testify

根据文档所说:"EqualErrorf断言一个函数返回了一个错误(即不是nil),并且它与提供的错误相等。"

assert.EqualErrorf(t, err, expectedErrorMsg, "Error should be: %v, got: %v", expectedErrorMsg, err)

断言错误消息包含一个子字符串:
assert.Containsf(t, err.Error(), tt.wantErrMsg, "expected error containing %q, got %s", tt.wantErrMsg, err)

它的类型是*testing.T,而err的类型是error

我认为这比当前接受的答案更好,因为我们正在检查是否抛出了特定的错误。如果抛出了不希望出现的错误,则仅检查err!= nil会隐藏此问题。 - Ed Harrod

12

我经常使用这个简单的函数来检查错误:

// ErrorContains checks if the error message in out contains the text in
// want.
//
// This is safe when out is nil. Use an empty string for want if you want to
// test that err is nil.
func ErrorContains(out error, want string) bool {
    if out == nil {
        return want == ""
    }
    if want == "" {
        return false
    }
    return strings.Contains(out.Error(), want)
}

示例用法:

if !ErrorContains(err, "unexpected banana") {
    t.Errorf("unexpected error: %v", err)
}

if !ErrorContains(err, "") {
    t.Errorf("unexpected error: %v", err)
}

我发现在基于表格的测试中,这样做尤其有用:

tests := struct{
    want    string
    wantErr string
}{
    {
        "some output",
        "",  // No error expected
    },
    {
        "",
        "out of coconuts",
    }
}
它避免了与 "nil"、"errors.New()" 等打交道的麻烦。
我使用 "string.Contains()" 而不是检查完整的错误,因为这更加稳健。我只想知道这是否是我预期的错误(而不是完全不相关的错误)。我很少检查完整的错误消息,而是使用关键字代替(例如“意外结束”,“不足”等)。
该函数是github.com/teamwork/test包的一部分(我是主要作者),但如果我只使用此函数而不使用该包中的其他函数,则通常只需复制/粘贴它。

7

我测试了返回错误并检查错误消息是否匹配的函数。但是是否检查错误仅为你决定,只需检查错误不是nil

假设您有这样一个函数:

func returnSomeErr(input int)error{
    if input > 0{
        return nil
    }
    return errors.New("this is error message")
}

您可以像这样对错误消息进行单元测试:
// testing tot get error message
func TestReturnSomeErr(t *testing.T){
   Expected := "this is error message"
   actual := returnSomeErr(-1)

   if actual.Error() != Expected{
        t.Errorf("Error actual = %v, and Expected = %v.", actual, test.Expected)
   }
}

请注意,我正在使用.Error()函数来获取错误消息,以便将其与字符串进行比较。您可以创建另一个测试以测试输入数据> 0时是否没有错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接