我正试图在我的项目中创建一个单元测试,在其中我模拟http客户端并设置客户端必须返回的响应. 我需要这样的行为,因为我的代码需要相应的行为,以防http客户端因超时而失败:因此,我需要模拟http客户端,以返回死线ExceededError,并对其进行单元测试.

到目前为止,我所try 的是以这样一种方式模拟客户端do函数,即返回client.Do:

GetDoFunc = func(*http.Request) (*http.Response, error) {
    return nil, &url.Error{
        Op:  "Post",
        Err: context.DeadlineExceeded,
    }
}

它可以工作,但不能完全工作,这意味着当我使用这种模拟行为执行代码时,返回的错误类型是:

error(*net/url.Error) *{Op: "Post", URL: "", Err: error(context.deadlineExceededError) {}}

这也是正确的,但并不完全正确.为什么?因为如果我运行代码,并且发生真正的超时,我会得到更完整的结果:

error(*net/url.Error) *{Op: "Post", URL: "http://localhost:4500/scan/", Err: error(*net/http.httpError) *{err: "context deadline exceeded (Client.Timeout exceeded while awaiting headers)", timeout: true}}

我最感兴趣的是那timeout: true美元.如果我设法告诉我的mock返回它,我可以断言这一点,我发现这比只断言返回的错误是deadlineExceededError类型的更完整.

推荐答案

为了不使测试过于复杂,我建议您使用这种方法.首先,定义您的错误:

type timeoutError struct {
    err     string
    timeout bool
}

func (e *timeoutError) Error() string {
    return e.err
}

func (e *timeoutError) Timeout() bool {
    return e.timeout
}

In this way, timeoutError implements both the Error() and Timeout interfaces.
Then you've to define the mock for the HTTP client:

type mockClient struct{}

func (m *mockClient) Do(req *http.Request) (*http.Response, error) {
    return nil, &timeoutError{
        err:     "context deadline exceeded (Client.Timeout exceeded while awaiting headers)",
        timeout: true,
    }
}

这只是返回上面定义的错误,并将nil作为HTTP.Response返回.最后,让我们看看如何编写一个样例单元测试:

func TestSlowServer(t *testing.T) {
    r := httptest.NewRequest(http.MethodGet, "http://example.com", nil)
    client := &mockClient{}

    _, err := client.Do(r)

    fmt.Println(err.Error())
}

If you debug this test and pause with the debugger on the err variable, you'll see the wanted result.
Thanks to this approach you can achieve what you need without bringing in any extra complexity. Let me know if works for you!

Go相关问答推荐

Go 1.22 net/http群组路由

Go协议缓冲区导入问题

在Golang中,@LATEST和@UPGRADE特殊查询有什么不同?

+在具有html/模板Golang的Base64中

如何在gofr发起的服务间调用请求中添加Authorization Header?

JetBrains Goland,禁用突出显示测试文件

如何在gofiber/websocket/v2中设置状态代码和原因

Go Gin:验证 base64

转到 bufio.Writer、gzip.Writer 并上传到内存中的 AWS S3

正确的 shell 程序进入 golang alpine docker 容器的入口点?

Neptune 在连接到启用 IAM 的 Neptune 实例时抛出握手错误错误

如何将 base64 编码的公钥转换为 crypto.PublicKey 或 ecdsa.PublicKey

也许在 golang 中包(字符串和字符串类型不匹配)

动态 SQL 集 Golang

regex.ReplaceAll 但如果替换则添加相同数量的字符

如何模仿联合类型

为什么 0 big.Int 的 .Bytes() 值是空切片?

Golang - 使用正则表达式提取链接

如何扩充 ResponseWriter 的 Header() 返回的 map

Golang LinkedList 删除第一个元素