httptest.server可快速启动真实http mock服务,需显式调用w.writeheader()设状态码、用闭包隔离测试状态、调用server.close()防端口占用,适用于单元测试但不支持tls和压测。

用 net/http/httptest 快速起一个可断言的 Mock Server
不需要额外依赖,Go 标准库自带的 httptest.Server 就够用了——它启动的是真实 HTTP 服务(绑定本地端口),不是纯内存 fake handler。适合需要被其他进程、客户端或跨 goroutine 调用的场景。
常见错误是直接传 http.HandlerFunc 给 httptest.NewServer 却忘了它内部会自动包装成 http.Handler,导致 handler 里无法访问 http.Request.URL.Path 或 query 参数解析失败。
- 用
http.NewServeMux()显式注册路由,比裸写http.HandlerFunc更可控 - 启动后记得调用
server.Close(),否则测试跑完端口可能被占住,后续测试失败 -
server.URL是带协议和端口的完整地址(如http://127.0.0.1:34212),别手动拼接路径
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/api/user" && r.Method == "GET" {
w.WriteHeader(200)
json.NewEncoder(w).Encode(map[string]string{"id": "123"})
return
}
http.Error(w, "not found", 404)
}))
defer server.Close() // 关键
Mock 不同 HTTP 方法和状态码时,别漏掉 w.WriteHeader()
Go 的 http.ResponseWriter 默认状态码是 200,且一旦有 body 写入(比如 json.NewEncoder(w).Encode(...))就自动发 200。想返回 404、500 或 201,必须在写 body 前显式调用 w.WriteHeader(),否则状态码永远是 200 —— 这是新手最常踩的坑。
- 状态码和 header 设置必须在第一次
w.Write()或encoder.Encode()之前完成 - 如果 handler 里用了
http.Error(w, msg, code),它内部已调用WriteHeader,不用再重复 - 测试中用
http.DefaultClient.Do(req)拿到的*http.Response,要检查resp.StatusCode,不能只看 body
需要动态响应逻辑?用闭包捕获变量,别用全局或包级变量
当 Mock Server 需要根据请求参数返回不同数据(比如按 query 参数返回不同用户),或记录调用次数做断言时,闭包是最干净的方式。用包级变量会导致多个测试用例互相污染,尤其是并行测试(t.Parallel())下必崩。
立即学习“go语言免费学习笔记(深入)”;
- 把计数器、期望值、返回数据封装进匿名函数的捕获变量里
- 避免在 handler 外部定义
var callCount int,它会被所有请求共享 - 如果需要验证请求体内容,用
io.ReadAll(r.Body)读一次,之后r.Body就空了,别反复读
callCount := 0
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
callCount++
if r.URL.Query().Get("id") == "admin" {
w.WriteHeader(200)
json.NewEncoder(w).Encode(map[string]string{"role": "admin"})
}
}))
// 后续可 assert: callCount == 1
生产环境别用 httptest.Server,它不支持 TLS 和长连接压测
httptest.Server 是为单元测试设计的,底层用的是 net.Listen("tcp", "127.0.0.1:0"),端口随机、无 TLS、无超时控制、不复用连接。如果你在写集成测试或需要模拟真实网关行为(比如重定向、header 转发、证书校验),它就不够用了。
- 需要 HTTPS?换
golang.org/x/net/http2+ 自签证书,或改用mockserver-go这类第三方工具 - 要测连接池、Keep-Alive 行为?得用真实
http.Server并手动管理 listener - 并发量大时,
httptest.Server的单 goroutine handler 可能成为瓶颈,真实服务更接近线上表现
真正难的不是起个 Mock Server,而是让它的行为边界足够清晰:什么时候该用标准库,什么时候该切出去——尤其当测试开始依赖外部状态、时间、网络延迟时,mock 的“假”就容易变成 bug 的温床。










