简体   繁体   English

golang 单元测试中的 Mocking 特定错误类型

[英]Mocking specific error types in golang unit tests

I have been working on handling soft failures in some golang functions, and want to create some unit tests for the various failure scenarios.我一直致力于处理一些 golang 函数中的软故障,并希望为各种故障场景创建一些单元测试。 For example some of the checks include:例如,一些检查包括:

        switch err := err.(type) {
        case net.Error:
                if err.(net.Error).Temporary() || err.(net.Error).Timeout() {
                        ...
                }
        case *url.Error:
                if err, ok := err.Err.(net.Error); ok && err.Timeout() {
                        ...
                }
        case *net.OpError:
                if syscallErr, ok := err.Err.(*os.SyscallError); ok {
                        if syscallErr.Err == syscall.ECONNRESET {
                                ...
                        }
                }
        ...

I believe I just need my mock function to return errors compatible with those conditions.我相信我只需要我的模拟 function 来返回与这些条件兼容的错误。 I see net.Error, net.OpError, url.Error, and os.SyscallError types exported.我看到 net.Error、net.OpError、url.Error 和 os.SyscallError 类型已导出。 However I can't find any examples of anyone constructing a net.Error value and returning it - people put a lot of work into creating an external processes that fail in various ways but no examples of returning a net.Error with a Timeout function that returns true.但是,我找不到任何人构造 net.Error 值并返回它的示例 - 人们投入了大量工作来创建以各种方式失败的外部进程,但没有返回 net.Error 超时 function 的示例返回真。

Likewise I could not find any examples of wrapping an error that wasn't produced with fmt.Errorf so I'm unsure if something more then errors.Wrap() is involved when not using the base builtin error type.同样,我找不到任何包装不是由 fmt.Errorf 产生的错误的示例,所以我不确定在不使用基本内置错误类型时是否涉及更多的错误。Wrap()。

https://golang.org/pkg/net/#Error is an interface , and defining Timeout and Temporary would be trivial since they just return bool . https://golang.org/pkg/net/#Error是一个interface ,定义TimeoutTemporary将是微不足道的,因为它们只是返回bool

package main

import(
        "fmt"
        "net"
)

type mockNetError struct {
        temporary bool
        timeout bool
}

func (e *mockNetError)Error() string {
        return fmt.Sprintf("mockNetworkError: timeout %b, temporary %b", e.timeout, e.temporary)
}

func (e *mockNetError)Temporary() bool{
        return e.temporary
}

func (e *mockNetError)Timeout() bool{
        return e.timeout
}
func main() {
        var err error = new(mockNetError)
        if nerr, ok := err.(net.Error); ok {
                fmt.Println("It was a network error! %s", nerr.Error())
        }
}

However, because net.Error is an interface type, err.(type) will never return it.但是,因为net.Error是一个接口类型,所以err.(type)永远不会返回它。 In my code, I assert that it might satisfy that interface, which is different from asking whether it is the type of the value in the err interface.在我的代码中,我断言它可能满足该接口,这与询问它是否是err接口中的值的类型不同。

The other errors you mention from net are not interfaces (though they do satisfy net.Error ).您从net提到的其他错误不是接口(尽管它们确实满足net.Error )。

https://blog.golang.org/go1.13-errors talks about how error wrapping works in go. https://blog.golang.org/go1.13-errors讨论了错误包装在 go 中的工作原理。

a convention rather than a change: an error which contains another may implement an Unwrap method returning the underlying error.约定而不是更改:包含另一个错误的错误可能会实现返回底层错误的 Unwrap 方法。

the reason %w is almost always the way it's done is because folks are almost always adding additional text to the error. %w几乎总是这样做的原因是因为人们几乎总是在错误中添加额外的文本

In fact, net.OsError is an example of an error that can wrap another by providing an Unwrap() method.事实上, net.OsError是一个可以通过提供Unwrap()方法包装另一个错误的示例。

If you want to create such an error, just:如果您想创建这样的错误,只需:

fmt.Println(net.OpError{
   Op: "mock", 
   Net: "mock",
   Source: &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 1234 },                
   Addr: &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 12340 },
   Err: fmt.Errorf("Mock net.OpError"),
})

Building on the answer from Daniel Farrell, I found an example of mocking an os.SyscallError buried deep in the AWS SDK for Golang.基于 Daniel Farrell 的回答,我发现了一个 mocking 和 os.SyscallError 的示例,它深埋在 Golang 的 AWS SDK 中。 Putting them together we get this:把它们放在一起,我们得到这个:

return nil, &net.OpError{
                Op:     "mock",
                Net:    "mock",
                Source: &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 1234},
                Addr:   &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 12340},
                Err:    &os.SyscallError{Syscall: "read", Err: syscall.ECONNRESET},
               }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM