我可以为我的测试用例编写我自己版本的 go 的 http client.Do() 函数吗?

Can I write my own version of go's http client.Do() function for my test cases?

我有一个名为 user-service.go 的文件和相应的测试文件,名为 user-service_test.go。当我试图获得完整的代码覆盖率时,我正在努力让一些错误条件真正发生。

函数如下:GetOrCreateByAccessToken()

//GetOrCreateByAccessToken gets a user from the database with the given access token
func (s *service) GetOrCreateByAccessToken(aT string, client *Client) (*user.User, fcerr.FCErr) {

var currentUser user.OauthUser

req, err := http.NewRequest("GET", "https://openidconnect.googleapis.com/v1/userinfo?access_token="+aT, nil)
if err != nil {
    return nil, fcerr.NewInternalServerError("Error when setting up the network request")
}

response, err := client.httpClient.Do(req)
if err != nil {
    fmt.Println("error when getting the userinfo with the access token")
    return nil, fcerr.NewInternalServerError("Error when trying to verify user identity")
}

defer response.Body.Close()

contents, err := io.ReadAll(response.Body)
if err != nil {
    return nil, fcerr.NewInternalServerError("Error when trying to read response from Google about user identity")
}

我对测试的主要控制是我可以传入 *Client。

这是我希望 io.ReadAll 抛出错误的测试用例部分:

h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    //manually return the message google would return on an actual request
    w.Write([]byte(googleAPIOKResponse))
})
//Call the testHTTPClient() function defined in the test file to substitute my own HandlerFunc
httpClient, teardown := testHTTPClient(h)
defer teardown()

//Call the real NewClient() from my user-service.go
client := NewClient()

//Substitute the default httpClient for the one I've just set up.
client.httpClient = httpClient

resultingUser, err := userService.GetOrCreateByAccessToken(nU.AccessToken, client)

assert.Nil(t, resultingUser)
assert.NotNil(t, err)
assert.Equal(t, http.StatusInternalServerError, err.Status())

有没有什么地方可以让我编写自己的 .Do() 方法版本,该方法会在响应中放置一些内容,从而导致 io.ReadAll 到 return 错误?或者是否有更好的方法来仅使用我已经在使用的预烘焙响应文本来解决错误?

没有替代 Do 方法的方法,但有一种方法可以实现您的目标。

创建一个 round tripper 类型 returns 任意响应主体:

type respondWithReader struct{ body io.Reader }

func (rr respondWithReader) RoundTrip(req *http.Request) (*http.Response, error) {
    return &http.Response{
        Proto:      "HTTP/1.0",
        ProtoMajor: 1,
        Header:     make(http.Header),
        Close:      true,
        Body:       ioutil.NopCloser(rr.body),
    }, nil

}

创建失败的 io.Reader:

var errReadFail = errors.New("blah!")

type failReader int

func (failReader) Read([]byte) (int, error) {
    return 0, errReadFail
}

将库存客户端与上面的传输和 reader 一起使用:

c := http.Client{Transport: respondWithReader{body: failReader(0)}}
resp, err := c.Get("http://whatever.com")
if err != nil {
    t.Error(err)
}
defer resp.Body.Close()

// ReadAll returns errReadFail
_, err = ioutil.ReadAll(resp.Body)
if err != errReadFail {
    t.Errorf("got err %v, expect %v", err, errReadFail)
}

Run the test on the Go playground.