英文:
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 (
"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)
}
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 interface
s.
https://play.golang.org/p/3RFrxKGkdm
Also read the following blog about errors.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论