我在Go中使用httptest包来测试我的应用程序。最近我注意到我的一个测试未能完成,因为我的测试没有阅读回复的主体
func Test_AnyTest(t *testing.T) {
serve := newServer()
s := httptest.NewServer(serve)
defer s.Close()
testUrl := "/any/old/url"
c := &http.Client{}
r, _ := http.NewRequest("GET", s.URL+testUrl, new(bytes.Buffer))
r.Header.Add("If-None-Match", cacheVersion)
res, _ := c.Do(r)
if res.StatusCode == 304 {
t.Errorf("Should not have got 304")
}
}
上面的代码阻止了对s.Close()
的延迟调用,因为测试服务器上有未完成的http连接。我的服务器有一些写入正文的代码(使用http.ResponseWriter接口)。原来这个代码实际上是阻塞的,直到我在测试中读取了正文。
这样的电话就行了。
ioutil.ReadAll(res.Body)
我不介意在我的测试中进行此调用,但我担心行为不当的客户端可能会导致此行为并消耗服务器资源。有谁知道这里发生了什么?这是预期的行为还是仅仅是测试框架的问题?
谢谢!
答案 0 :(得分:2)
来自http
文档:
完成后,客户端必须关闭响应正文:
resp, err := http.Get("http://example.com/") if err != nil { // handle error } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) // ...
因此,您的测试违反了http.Client
的合同;添加defer res.Close()
应该让服务器知道响应通道已关闭,并停止写入它。 ioutil.ReadAll
的效果是相同的,因为一旦达到EOF就会关闭Reader
。
在野外,如果客户端打开连接并发送请求,则服务器只发送响应然后关闭连接。它不会等待客户端关闭它。如果客户端太慢而无法确认服务器发送的IP数据包,操作系统最终会关闭连接超时,http.Server
将通过退出服务请求的goroutine来响应,没有小猫受到伤害。