Go闭包死锁

huangapple go评论70阅读模式
英文:

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)
    }))
}

huangapple
  • 本文由 发表于 2015年10月12日 09:10:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/33071854.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定