【译】Go 中 httptest 包的强大之处

注:本文译自 The awesomeness of the httptest package in Go

Go 有一个很好的 http 包。我之所以能这么说,是因为除了标准库提供的实现之外,我不知道 Go 中有任何其他实现。这对我来说是一个好兆头。

1
2
3
4
5
6
resp, err := http.Get("http://example.com/")
if err != nil {
// handle error
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)

这个例子来自 go 文档

我们来这里是为了阅读有关测试的内容,所以谁关心 http 包本身!重要的是 httptest 包!酷多了。

服务器端

http 包提供了客户端和服务器。服务器由处理程序组成,处理程序接受请求并根据该请求返回响应。

这是它的界接口定义:

1
2
3
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}

正如您所看到的,它根据收到的请求通过 ResponseWriter 来返回响应。在这个过程中,它可能读数据库、调用第三方服务,但最终,它会将结果写到一个响应。

这意味着模拟所有依赖项将获得正确的场景,我们使用 ResponseWriter 来确定处理程序是否满足了我们的要求。

httptest 包提供了 ResponseWriter替身,称为 ResponseRecorder。我们可以将它传递给处理程序并检查它执行后的样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
handler := func(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "ping")
}

req := httptest.NewRequest("GET", "http://example.com/foo", nil)
w := httptest.NewRecorder()
handler(w, req)

resp := w.Result()
body, _ := ioutil.ReadAll(resp.Body)

fmt.Println(resp.StatusCode)
fmt.Println(string(body))

这个处理程序非常简单,它只是操作响应主体。如果您的处理程序更复杂并且具有依赖项,您还必须确保替换它们,注入适当的处理程序。

客户端

Go http 包还提供了一个 http 客户端,您可以使用它与 http 服务器交互。http 客户端本身是无用的,但它是对通过 HTTP 获取的信息进行所有操作和转换的入口点。随着微服务的激增,这是一种非常普遍的情况。

工作流程很好理解,您有一个可以与之交互的 HTTP 后端,您可以从那里获取数据,然后使用业务逻辑来操作它们。在测试时,您可以做的就是模拟 http 后端以返回您想要的内容,测试您的业务逻辑是否根据从 HTTP 服务器获得的输入执行其应该执行的操作。

在我们的第一个示例中,处理程序是我们测试的主题,但情况不再是这样,这次我们测试客户端,因此我们必须模仿处理程序才能获得我们期望返回的内容:

1
2
3
4
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "I am a super server")
}))
defer ts.Close()

正如您所看到的,我们正在通过 httptest 创建一个新的 HTTP 服务器。它接受一个处理程序,该处理程序的目标是返回我们想要获取代码的内容。理论上,它应该只使用 ResponseWriter 来编写我们期望的响应。

服务器有一堆方法,你要找的就是 URL 方法。因为我们可以将它传递给 http.Client,我们将使用它作为函数的模拟:

1
2
3
4
5
6
res, err := http.Get(ts.URL)
if err != nil {
log.Fatal(err)
}
bb, err := ioutil.ReadAll(res.Body)
res.Body.Close()

就是这样,正如您所看到的,ts.URLhttp.Client 指向我们创建的模拟服务器。

结论

我经常使用 httptest 包,因为我可以遵循他们的文档来模拟他们的服务器,并且在我对自己编写的代码充满信心之前,我不需要接触他们。

我的建议是测试您的客户端代码是否存在边缘情况,因为 httptest.Server 使您可以灵活地编写您可以想到的任何响应:您可以模仿授权响应来查看代码如何处理它,或者返回空正文或添加速率限制。

彦祖老师 wechat