net/http/httptest 是 Go 中最标准、轻量且可靠的 HTTP 接口单元测试方式,通过内存管道直接调用 handler,无需网络开销;常用 httptest.NewRecorder 验证响应,NewServer 模拟完整客户端行为。
使用 net/http/httptest 是 Go 中进行 HTTP 接口单元测试最标准、轻量且可靠的方式。它不启动真实网络服务,而是通过内存中的请求-响应管道直接调用你的 handler,速度快、隔离性好、无需端口占用。
核心是把你的 HTTP handler(比如一个 http.HandlerFunc 或实现了 http.Handler 接口的结构体)传给 httptest.NewServer 或直接用 httptest.NewRecorder 配合 handler.ServeHTTP 调用。
httptest.NewRecorder() + 手动调用 ServeHTTP
httptest.NewServer(handler),它会启动一个临时监听地址假设你有如下 handler:
func HelloHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]string{"message": "hello"}) }
对应测试写法:
func TestHelloHandler(t *testing.T) {
req := httptest.NewRequest("GET", "/hello", nil)
w := httptest.NewRecorder()
HelloHandler(w, req) // 直接调用,无网络开销
if w.Code != http.StatusOK {
t.Fatalf("expected status OK, got %d", w.Code)
}
if w.Header().Get("Content-Type") != "application/json" {
t.Error("Content-Type header not set correctly")
}
var resp map[string]string
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
t.Fatal(err)
}
if resp["message"] != "hello" {
t.Error("unexpected message")
}
}
httptest.NewRequest 支持构造任意 URL 和请求头:
"/api/users?id=123&name=john"
/:id):需配合路由库(如 gorilla/mux 或 chi)解析;net/http 原生不解析,需手动设置 r.URL.Path 或使用其 URL.Query() 提取参数strings.NewReader(`{"name":"test"}`) 作为第三参数,并设置 Content-Type: application/json
如果 handler 依赖数据库、配置或上下文值,推荐将依赖抽象为字段或参数,测试时传入 mock 实现:
type UserHandler struct {
Store UserStore // 接口类型
}
func (h *UserHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
user, _ := h.Store.Get(r.URL.Query().Get("id"))
json.NewEncoder(w).Encode(user)
}
测试时传入一个实现 UserStore 的 fake 结构体即可,无需真实 DB 连接。
w.Code —— 默认是 200,但 handler 可能返回 404/500 等w.Body.String() 前没确认编码是否正确(特别是中文),建议统一用 w.Body.Bytes() 解析NewServer 后未调用 .Close(),可能导致 goroutine 泄漏(尤其在循环测试中)httptest 能测试 TLS、代理、DNS 等网络层行为 —— 它只模拟应用层逻辑不复杂但容易忽略细节,掌握 NewRecorder 和 NewRequest 的组合就能覆盖绝大多数接口测试场景。