测试非不透明错误值

huangapple go评论78阅读模式
英文:

Testing non-opaque error values

问题

在Go语言中,比较非透明错误值是否有最佳实践?

大多数代码库似乎将错误视为不透明的(要么操作成功,要么失败,没有对导致错误的内部细节的可见性)。

这使得编写单元测试变得很容易,因为你只需要将实际错误与预期错误进行断言。我见过的最多的做法是将错误字符串进行比较,以确保至少包含一些关键信息。例如:

if err == nil || !strings.Contains(err.Error(), "not found in the Raft configuration") {
  t.Fatalf("err: %v", err)
}

但是对于需要额外错误信息的情况(例如表单验证,需要指定无效字段名称、值、错误代码和可能的嵌套错误),你该怎么办?

我不能简单地使用reflect.DeepEqual(),因为错误会包含唯一的堆栈跟踪,这将导致每次比较失败。此外,有嵌套错误会使测试逻辑变得更加复杂,你需要使用各种容易出错的逻辑(循环、递归)来比较所需的字段。是否有一种标准的方法来测试这种类型的错误?

英文:

Is there a best practice for comparing non-opaque error values in Go?

Most code bases seem to treat errors as opaque (either the operation succeeded or it failed, with no visibility into internal details about what caused the error).

This makes it easy to write unit tests since all you need to do is assert the actual error against the expected error. The most I've seen people do in addition to that is compare error strings to make sure that it includes at minimum some key information. For example:

if err == nil || !strings.Contains(err.Error(), "not found in the Raft configuration") {
  t.Fatalf("err: %v", err)
}

But for cases where the additional error information is needed (like in form validation where you need to specify the invalid field name, the value, an error code and possibly some nested errors too), what do you do?

I can't simply do a reflect.DeepEqual() since the error will contain a unique stack trace which will make the comparison fail every time. Also, having nested errors makes the testing logic even more complex and you'll need all kinds of error-prone logic (loops, recursion) to compare the desired fields. Is there a standard way of testing errors of this nature?

答案1

得分: 1

虽然这可能不能回答你关于检查错误的方式不正确的问题,但至少这是一个如何正确创建和使用错误的示例。

在标准库包中通常的做法是将错误创建为导出变量,然后可以抛出和比较这些错误。这里有一个示例...尽管可能不太有创意。

package main

import (
	"errors"
	"fmt"
)

var ErrBadIdea = errors.New("that was dumb")
var ErrNoValue = errors.New("not a real value")

func DoSomething(i int) error {
	if i == 0 {
		return ErrNoValue
	}
	if i < 0 {
		return ErrBadIdea
	}
	// does something that doesn't like negative or zero values
	return nil
}

func main() {
	err := DoSomething(-1)
	fmt.Println(err == ErrBadIdea, err)
	err = DoSomething(0)
	fmt.Println(err == ErrNoValue, err)
}

在标准库的database/sql包中有一个很好的例子。

这些变量存在,所以你可以像这样进行测试:

if err == sql.ErrNoRows { // 这些不是你要找的行 }

更棒的是!

因为error是一个接口,你只需要实现它。只需要有一个func Error() string函数。看看下面的示例,惊叹于接口的威力。

package main

import (
	"fmt"
)

type MyError struct {
	Msg string
}

func (e MyError) Error() string {
	return e.Msg
}

func main() {
	err := MyError{Msg: "This is an error"}
	fmt.Println(err.Error())
}

还可以阅读以下关于错误处理的博客文章。

https://blog.golang.org/error-handling-and-go

英文:

While this may not answer your question pertaining to checking errors that are not done the right way, here is at least an example of how to create and use errors the right way.

What is commonly done in the standard library packages is errors are created as exported variables and then those can be thrown and compared. Here is an example... however unimaginative it may be.

https://play.golang.org/p/JlpguFn2NX

package main

import (
	&quot;errors&quot;
	&quot;fmt&quot;
)

var ErrBadIdea = errors.New(&quot;that was dumb&quot;)
var ErrNoValue = errors.New(&quot;not a real value&quot;)

func DoSomething(i int) error {
	if i == 0 {
		return ErrNoValue
	}
	if i &lt; 0 {
		return ErrBadIdea
	}
	// does something that doesn&#39;t like negative or zero values
	return nil
}

func main() {
	err := DoSomething(-1)
	fmt.Println(err == ErrBadIdea, err)
	err = DoSomething(0)
	fmt.Println(err == ErrNoValue, err)
}

An excellent example of this is in the database/sql package of the standard library.

https://godoc.org/database/sql#pkg-variables

These variables exist so you can test like so

if err == sql.ErrNoRows { // these are not the rows you are looking for }

It gets better!

Because error is an interface, all you have to do is implement it. Which only requires you have a func Error() string. See the following example and marvel at the power of interfaces.

https://play.golang.org/p/3RFrxKGkdm

Also read the following blog about errors.

https://blog.golang.org/error-handling-and-go

huangapple
  • 本文由 发表于 2017年4月5日 02:19:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/43214776.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定