httptest 是 Go 测试 HTTP 接口最直接可靠的方式:内存中完成请求响应,不占端口不依赖网络;单元测试首选 httptest.NewRequest + httptest.NewRecorder,集成测试需用 httptest.NewServer。

httptest 是 Go 测试 HTTP 接口最直接、最可靠的方式——它不启动真实服务器,不占端口,也不依赖网络,所有请求响应都在内存中完成。
用 httptest.NewRequest + httptest.NewRecorder 测 handler 函数
这是 90% 的单元测试场景该选的方式:快、隔离、可控。
- 构造请求:
http.NewRequest("POST", "/api/users", strings.NewReader(`{"name":"alice"}`)),记得设Content-Type: application/json - 捕获响应:
w := httptest.NewRecorder(),它实现了http.ResponseWriter,但只往内存写 - 直接调用 handler:
myHandler(w, req),不是发网络请求,而是函数调用 - 断言时别只看状态码:
w.Code == http.StatusCreated,更要检查w.Header().Get("Content-Type")和json.Unmarshal(w.Body.Bytes(), &resp)后字段值是否正确 - 常见错误:忘了设置
req.Header.Set("Content-Type", ...),导致 handler 解析 body 失败;或用w.Body.String()做字符串匹配,而 JSON 字段顺序/空格/换行不固定,应优先解码后比对结构
用 httptest.NewServer 测客户端或集成行为
当你在测一个 *http.Client、SDK 封装、或需要重定向/Cookie/多次请求的逻辑时,必须用它。
- 它会自动监听一个空闲端口,返回一个
*httptest.Server,其URL字段可直接传给 client - 务必
defer server.Close(),否则端口泄漏,连续跑测试会 panic - 不要硬编码
"http://localhost:8080",要用server.URL + "/path" - 适合验证 client 是否设置了正确 header、是否处理了 401、是否重试了 503——这些行为只有真实 round-trip 才能触发
- 性能开销略大(毕竟要走 TCP 栈模拟),所以仅用于“必须走 client 路径”的测试,别滥用
测带路径参数或查询参数的接口
Go 原生 http.ServeMux 不解析 /:id,路由库(如 gorilla/mux、chi)才做这步。测试时得按实际解析方式准备请求。
- 查询参数:直接拼进 URL,比如
http.NewRequest("GET", "/users?id=123&role=admin", nil),handler 中用r.URL.Query().Get("id") - 路径参数:若用
gorilla/mux,需手动注入变量:mux.SetURLVars(req, map[string]string{"id": "789"}) - 别指望
r.URL.Path自动被“解析”成参数——那是路由层的事,handler 层看到的仍是原始路径字符串 - 如果 handler 里混用了
r.URL.Query()和r.URL.Path提取逻辑,测试就得覆盖两种构造方式,否则漏掉边界 case
测试失败时最容易忽略的一点
很多测试只验证“返回了什么”,却没验证“不该发生什么”。比如 handler 本该调用一次数据库 Insert(),但测试没 mock、也没断言调用次数,结果逻辑出错也通过。
- 把外部依赖(DB、缓存、第三方 client)抽成接口,测试时注入 mock 实现
- 在 mock 方法里加计数器或记录调用参数,测试末尾 assert 调用次数和参数是否匹配
- HTTP 层测试不是终点——状态码对、JSON 对,不代表业务逻辑对。真正的难点永远在“handler 内部是否按预期调用了依赖”










