英文:
Go closure deadlock
问题
尝试在Go测试中模拟HTTP响应。如果我使用以下命令运行它,代码片段将永远不会终止:
package auth_test
import (
"testing"
"net/http/httptest"
"net/http"
)
func TestAuthorization(t *testing.T) {
t.Log("当网关返回401时应返回401")
{
url := oneOffUrlWithResponseCode(http.StatusUnauthorized)
request, _ := http.NewRequest("GET", url, nil)
response, _ := http.DefaultClient.Do(request)
if response.StatusCode != http.StatusUnauthorized {
t.Fatalf("响应应为401(未授权)")
}
}
}
func oneOffUrlWithResponseCode(responseCode int) string {
var server *httptest.Server
server = httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
defer server.Close()
response.WriteHeader(responseCode)
}))
return server.URL
}
然而,如果我注释掉这一行:
defer server.Close()
一切都正常工作。
理想情况下,我不想在oneOffUrlWithResponseCode
函数之外"泄漏" *httptest.Server
,并且显然在第一次请求之后关闭它。
为什么它永远不会终止?我做错了什么?正确的做法是什么?
英文:
Trying to mock http response in Go test. The code snippet below never terminates if I run it with
> go test example.com/auth/...
package auth_test
import (
"testing"
"net/http/httptest"
"net/http"
)
func TestAuthorization(t *testing.T) {
t.Log("Should return 401 when Gateway returns 401")
{
url := oneOffUrlWithResponseCode(http.StatusUnauthorized)
request, _ := http.NewRequest("GET", url, nil)
response, _ := http.DefaultClient.Do(request)
if response.StatusCode != http.StatusUnauthorized {
t.Fatalf("Response should be 401 (Unauthorized)")
}
}
}
func oneOffUrlWithResponseCode(responseCode int) string {
var server *httptest.Server
server = httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
defer server.Close()
response.WriteHeader(responseCode)
}))
return server.URL
}
However, if I comment out this line
> defer server.Close()
everything works fine.
Ideally, I do not want to "leak" *httptest.Server outside of oneOffUrlWithResponseCode function and obviously close it after first request.
Why it never terminates? What am I doing wrong? What is the right thing to do?
答案1
得分: 4
程序不会因为死锁而终止(与闭包无关)。你不能在处理程序内部调用Close
,因为内部的Close
会等待所有处理程序完成。
修复它的最简单方法是将httptest.Server
“泄漏”到oneOffUrlWithResponseCode
之外:
func TestAuthorization(t *testing.T) {
...
server := oneOffUrlWithResponseCode(http.StatusUnauthorized)
defer server.Close()
request, _ := http.NewRequest("GET", server.URL, nil)
...
}
func oneOffUrlWithResponseCode(responseCode int) *httptest.Server {
return httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
response.WriteHeader(responseCode)
}))
}
英文:
The program doesn't terminate because of a deadlock (and it has nothing to do with closures). You cannot call Close
inside a handler because internally Close
waits for all handlers to finish.
The easiest way to fix it is to "leak" httptest.Server outside of oneOffUrlWithResponseCode:
func TestAuthorization(t *testing.T) {
...
server := oneOffUrlWithResponseCode(http.StatusUnauthorized)
defer server.Close()
request, _ := http.NewRequest("GET", server.URL, nil)
...
}
func oneOffUrlWithResponseCode(responseCode int) *httptest.Server {
return httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
response.WriteHeader(responseCode)
}))
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论