Golang:用httptest拦截和模拟HTTP响应



我已经研究了可以在golang中用于模拟测试的各种不同的工具,但是我尝试使用httptest来完成这项任务。特别是,我有这样一个函数:

type contact struct {
username string
number int
}
func getResponse(c contact) string {
url := fmt.Sprintf("https://mywebsite/%s", c.username)
req, err := http.NewRequest(http.MethodGet, url, nil)
// error checking

resp, err := http.DefaultClient.Do(req)
// error checking

return response
}

我读过的很多文档似乎都需要创建客户端接口或自定义传输。是否没有办法在测试文件中模拟响应而不改变主代码?我希望将我的客户端、响应和所有相关细节保留在getResponse函数中。我可能有错误的想法,但我正试图找到一种方法来拦截http.DefaultClient.Do(req)调用并返回自定义响应,这是可能的吗?

我读过似乎需要创建一个客户端接口

不改变主代码

保持你的代码干净是一个很好的实践,你最终会习惯它,一个可测试的代码更干净,更干净的代码更可测试,所以不用担心改变你的代码(使用接口),以便它可以接受模拟对象。


你的代码最简单的形式可以是这样的:

package main
import (
"fmt"
"net/http"
)
type contact struct {
username string
number   int
}
type Client interface {
Do(req *http.Request) (*http.Response, error)
}
func main() {
getResponse(http.DefaultClient, contact{})
}
func getResponse(client Client, c contact) string {
url := fmt.Sprintf("https://mywebsite/%s", c.username)
req, _ := http.NewRequest(http.MethodGet, url, nil)
// error checking
resp, _ := http.DefaultClient.Do(req)
// error checking and response processing
return response
}

你的测试可以像这样:

package main
import (
"net/http"
"testing"
)
type mockClient struct {
}
// Do function will cause mockClient to implement the Client interface
func (tc mockClient) Do(req *http.Request) (*http.Response, error) {
return &http.Response{}, nil
}
func TestGetResponse(t *testing.T) {
client := new(mockClient)
getResponse(client, contact{})
}

但是如果你更喜欢使用httptest:

package main
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
)
type contact struct {
username string
number   int
}
func main() {
fmt.Println(getResponse(contact{}))
}
func getResponse(c contact) string {
// Make a test server
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "your response")
}))
defer ts.Close()
// You should still set your base url
base_url := ts.URL
url := fmt.Sprintf("%s/%s", base_url, c.username)
req, _ := http.NewRequest(http.MethodGet, url, nil)
// Use ts.Client() instead of http.DefaultClient in your tests.
resp, _ := ts.Client().Do(req)
// Processing the response
response, _ := io.ReadAll(resp.Body)
resp.Body.Close()
return string(response)
}

https://pkg.go.dev/net/http/httptest#example-Server对于您的用例来说是一个很好的例子,它对您的代码进行了一个小的重构。

您只需通过getResponse(url字符串)更改getResponse()即可为服务器提供模拟url。

最新更新