如何使用Go的httptest模拟多个不同的HTTP响应
解决方案
你可以通过闭包让测试服务的handler持有请求计数和预定义的响应序列,每处理一次请求就切换返回的内容,不需要修改现有工具函数,仅调整main函数和服务初始化逻辑即可,修改后的完整代码如下:
package main import ( "context" "fmt" "io" "net/http" "net/http/httptest" ) func buildMyRequest(ctx context.Context, url string) *http.Request { request, _ := http.NewRequestWithContext(ctx, "GET", url, nil) return request } func myPrint(response *http.Response) { b := make([]byte, 60000) n := 0 for { readN, err := response.Body.Read(b[n:]) n += readN if err == io.EOF { break } } // 仅输出实际读取的有效字节,避免打印冗余空字符 fmt.Println(string(b[:n])) // 请求结束后关闭Body避免资源泄漏 response.Body.Close() } func main() { // 按请求顺序预定义对应的响应内容 responses := []string{ `{"A":"B"}`, `{"C":"D"}`, `{"E":"F"}`, } reqCount := 0 // 定义带闭包的handler,捕获外部的响应序列和请求计数器 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) resp := responses[reqCount] // 计数器自增,超过序列长度后默认返回最后一个响应 if reqCount < len(responses)-1 { reqCount++ } w.Write([]byte(resp)) })) defer srv.Close() client := http.Client{} myResponse1, _ := client.Do(buildMyRequest(context.Background(), srv.URL)) fmt.Println("myResponse1:") myPrint(myResponse1) myResponse2, _ := client.Do(buildMyRequest(context.Background(), srv.URL)) fmt.Println("myResponse2:") myPrint(myResponse2) // 第三次请求测试 myResponse3, _ := client.Do(buildMyRequest(context.Background(), srv.URL)) fmt.Println("myResponse3:") myPrint(myResponse3) }
运行输出
myResponse1: {"A":"B"} myResponse2: {"C":"D"} myResponse3: {"E":"F"}
实现原理
闭包会捕获外部作用域的reqCount计数器和responses响应序列变量,handler每次被测试服务调用时,都会读取当前计数器对应的响应内容,随后自增计数器,实现不同请求返回不同响应的需求。如果需要更灵活的响应逻辑,也可以把计数器替换为请求路径、请求参数的匹配规则,适配更复杂的测试场景。
内容的提问来源于stack exchange,提问作者Saqib Ali
相关产品推荐
相关产品推荐

