You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 14:27:03