用 httptest.NewRecorder 测单个 handler,轻量快速;用 httptest.NewServer 测含中间件、路由或客户端行为的完整链路,需注意端口管理、依赖注入和执行顺序。

直接用 httptest.NewRecorder 测 handler,用 httptest.NewServer 测 client 或中间件链路——别一上来就起服务,90% 的单元测试用不到端口。
测单个 handler 函数:用 httptest.NewRequest + httptest.NewRecorder
这是最轻量、最常用的方式,适合验证路由逻辑、JSON 序列化、状态码等。不占端口、不走网络、执行快,还能断言内部调用(比如是否查了 DB)。
-
http.NewRequest构造请求时,body为nil表示空体;传 JSON 要用strings.NewReader(`{...}`)并显式设Content-Type: application/json -
httptest.NewRecorder返回的*httptest.ResponseRecorder不是http.ResponseWriter实现体,但兼容;可直接读.Code、.Body.Bytes()、.Header() - 常见错误:传
nil给handler.ServeHTTP导致 panic;或忽略 JSON 字段类型(map[string]interface{}中数字是float64)
req, _ := http.NewRequest("POST", "/login", strings.NewReader(`{"user":"a","pass":"123"}`))
req.Header.Set("Content-Type", "application/json")
rr := httptest.NewRecorder()
handler := http.HandlerFunc(LoginHandler)
handler.ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Errorf("expected status OK, got %d", rr.Code)
}
测完整服务链路(含中间件、路由、重定向):必须用 httptest.NewServer
当你用 Gin、Echo 或自定义 http.ServeMux,且中间件(如 JWT 鉴权、CORS、日志)参与响应流程时,NewRecorder 会跳过它们——必须把整个 handler 树丢给 NewServer。
-
NewServer启一个真实本地 HTTP 服务,返回带协议和随机端口的srv.URL(如"http://127.0.0.1:34212"),客户端代码必须用它,不能硬写"localhost:8080" - 每次调用都绑定新端口,务必
defer srv.Close(),否则测试间端口耗尽、goroutine 泄漏 - 不支持 HTTPS,默认只跑 HTTP;若需 TLS 测试,得用
httptest.NewUnstartedServer手动配srv.TLS
srv := httptest.NewServer(http.NewServeMux())
defer srv.Close() // 关键
resp, err := http.Get(srv.URL + "/api/user")
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
测 HTTP 客户端行为(比如重试、header 透传、跳转):也得用 httptest.NewServer
如果你在写一个封装了 http.Client 的 SDK 或工具函数,重点不是 handler 怎么处理,而是 client 怎么发、怎么收、怎么响应错误——这时 NewServer 是唯一可靠方式。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 别用
http.DefaultClient,它带全局连接池和默认超时,干扰测试可复现性;自己 new 一个干净的&http.Client{} - handler 里用
w.WriteHeader()和w.Write()精确控制响应,包括 302 跳转、401 拒绝、流式 body 等 - 容易被忽略:client 发出的请求可能带
ctx.WithTimeout,server handler 必须检查req.Context().Done(),否则测试卡死
中间件测试最容易漏掉的点:包装顺序和依赖注入
中间件本质是 func(http.Handler) http.Handler,测试时必须把目标 handler 包进链里再丢给 recorder/server,否则中间件逻辑完全没跑。
- 典型错误:直接测
LoggingMiddleware(UserHandler)却忘了传入一个实际的http.Handler做next,导致next.ServeHTTPpanic - 如果中间件依赖外部状态(如数据库连接、配置变量),测试前要重置或注入 mock,否则测试间污染
- 组合多个中间件时,执行顺序必须和线上一致;例如
Auth → CORS → UserHandler,测试时也要按这个顺序包装
真正难的不是写测试,是让 handler 和中间件本身可测试——比如避免直接引用全局 db 变量,改用依赖注入;否则你永远在 patch 全局状态,而不是验证逻辑。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










