httptest.NewRecorder 是模拟 ResponseWriter 的工具,用于捕获 handler 输出而非发起请求;需传给符合 func(http.ResponseWriter, *http.Request) 签名的 handler,并配合 httptest.NewRequest 构造含 body、header 和 query 的真实请求;status code 依赖 handler 显式调用 WriteHeader,否则 rr.Code 为 0;可测试中间件和路由,但需确保调用链完整。

怎么用 httptest.NewRecorder 捕获 handler 输出
它不是用来“发起请求”的,而是模拟一个响应写入目标——ResponseWriter 的替代品。你把它传给 handler,handler 调用 w.WriteHeader() 或 w.Write() 时,数据就存进 ResponseRecorder 里了,不走网络。
常见错误是直接拿 ResponseRecorder 当 *http.Response 用(比如想读 .Body),但它没有 Body 字段;得用 .Body.Bytes() 或 .Body.String() 取内容。
- 必须在调用 handler 前初始化:
rr := httptest.NewRecorder() - handler 签名必须是
func(http.ResponseWriter, *http.Request),别漏指针 - 记得传真实构造的
*http.Request,用httptest.NewRequest()最稳,别手写nil或空结构体
httptest.NewRequest 怎么构造带 body 和 header 的测试请求
很多 handler 依赖 Content-Type、Authorization 或 JSON body,光用 http.MethodGet 和路径不够。
httptest.NewRequest 第三个参数支持 io.Reader,所以 JSON 字符串要转成 strings.NewReader(jsonStr);如果 body 为空,传 nil 即可,但别传空字符串(会变成长度为 0 的 body,Content-Length: 0)。
立即学习“go语言免费学习笔记(深入)”;
- JSON 请求示例:
req := httptest.NewRequest("POST", "/api/user", strings.NewReader(`{"name":"a"}`)) - 设 header:
req.Header.Set("Content-Type", "application/json"),注意大小写不敏感但值要准确 - URL 查询参数用
req.URL.RawQuery = "id=123",别改req.URL.Path
为什么测试里 status code 总是 200 或 0
status code 是 handler 主动调用 w.WriteHeader(code) 写进去的。如果 handler 没调,rr.Code 默认是 0;如果 handler panic 或提前 return 没写状态码,Go http 包会在最后隐式写 200——但 ResponseRecorder 不会帮你补这个逻辑,它只记录你真正写的东西。
- 检查 handler 是否遗漏
w.WriteHeader(),尤其在 error 分支里 - 不要依赖 “没写就是 200”,测试中显式断言
assert.Equal(t, 400, rr.Code) - 如果 handler 用了第三方库(如 Gin、Echo),它们可能封装了
WriteHeader,但ResponseRecorder仍能捕获,没问题
测试中间件或路由时,ResponseRecorder 还够用吗
够用,但要注意调用链顺序。如果你测的是完整路由(比如用 http.ServeMux 或 gin.Engine),那就把整个 handler 链丢给 rr;如果只测单个中间件函数,它通常接收 http.Handler 并返回新 http.Handler,那你得手动构造下一层 handler 并传给它。
- 测 Gin handler:
router.ServeHTTP(rr, req),不用拆 - 测自定义中间件:
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}),再套myMiddleware(next) - 注意中间件可能修改
ResponseWriter(如加 header),这些修改都会反映在rr上
最易被忽略的是:handler 里调用 log.Print 或 fmt.Println 不影响 ResponseRecorder,但若用了 panic 且没 recover,测试会直接失败——这时候 rr 里什么都没有,Code 是 0,Body 是空。










