英文:
Create a deadlineExceededError for unit tests with timeout: true
问题
我正在尝试在我的项目中创建一个单元测试,其中我模拟了HTTP客户端并设置了客户端必须返回的响应。
我需要这样的行为,因为如果HTTP客户端由于超时而失败,我的代码需要相应地进行处理:因此,我需要模拟HTTP客户端返回一个deadlineExceededError,并对其进行单元测试。
到目前为止,我尝试的方法是以这样的方式模拟客户端的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
。如果我设法告诉我的模拟返回它,我就可以进行断言,这比仅仅断言返回的错误类型为deadlineExceededError更完整。
英文:
I am trying to create a unit test in my project where I mock the http client and setup the response that the client has to return.
I need such behaviour because my code needs to behave accordingly in case the http client fails due to timeout: hence I need to mock the http client to return a deadlineExceededError and make a unit test out of it.
What I tried so far was to mock the client Do function in such a way that client.Do returns:
GetDoFunc = func(*http.Request) (*http.Response, error) {
return nil, &url.Error{
Op: "Post",
Err: context.DeadlineExceeded,
}
}
it works but not fully, meaning that when I execute the code with such mocked behaviour, the kind of error returned is:
error(*net/url.Error) *{Op: "Post", URL: "", Err: error(context.deadlineExceededError) {}}
which again is correct, but not fully. Why? Because if I run the code and a real timeout happens I get something more complete:
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}}
what interests me the most is that timeout: true
. If I manage to tell my mock to return it, I could assert that, which I find it more complete than asserting only that the returned error is of type deadlineExceededError.
答案1
得分: 1
为了不过于复杂化测试,我建议你采用以下方法。首先,开始定义你的错误类型:
type timeoutError struct {
err string
timeout bool
}
func (e *timeoutError) Error() string {
return e.err
}
func (e *timeoutError) Timeout() bool {
return e.timeout
}
这样,timeoutError
实现了 Error()
和 Timeout
接口。然后,你需要定义 HTTP 客户端的模拟对象:
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())
}
如果你在这个测试中使用调试器并在 err
变量上暂停,你将看到期望的结果。通过这种方法,你可以在不引入任何额外复杂性的情况下实现你的需求。如果对你有帮助,请告诉我!
英文:
To not over-complicated the test too much, I'll suggest you this approach. First, start by defining your error:
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,
}
}
This simply returns the error defined above and nil
as the http.Response. Lastly, let's see how you can write a sample unit test:
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!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论