Go接口测试关键在于用httptest.Server验证真实HTTP流程或httptest.NewRequest+Recorder快速测handler逻辑,结合依赖注入与窄接口抽象外部依赖。

Go 的接口测试不需要框架也能做得很扎实,关键在于理解 http.HandlerFunc 和依赖注入的边界——测试时替换的是「行为」,不是「类型」。
用 httptest.Server 测试真实 HTTP 流程
当你要验证中间件顺序、状态码、响应头或请求体解析逻辑时,httptest.Server 是最贴近生产环境的选择。它启动一个真实监听的本地服务器,但不占端口(自动分配空闲端口)。
常见错误是直接传 nil 给 http.ListenAndServe 做测试,这会阻塞主线程且无法控制生命周期。
- 始终用
httptest.NewServer包裹你的http.Handler,别自己调ListenAndServe - 记得在测试末尾调用
server.Close(),否则 goroutine 泄漏 - 如果 handler 依赖外部服务(如数据库),先用依赖注入把 client 替换为 mock,再传给 handler 构造函数
func TestUserCreate(t *testing.T) {
mockDB := &MockUserDB{}
handler := NewUserHandler(mockDB) // 依赖注入 DB client
server := httptest.NewServer(handler)
defer server.Close()
resp, _ := http.Post(server.URL+"/users", "application/json", strings.NewReader(`{"name":"a"}`))
if resp.StatusCode != http.StatusCreated {
t.Errorf("expected 201, got %d", resp.StatusCode)
}
}
用 httptest.NewRequest + httptest.NewRecorder 测试单个 handler 函数
适合快速验证路由匹配、参数解析、业务逻辑分支。不走网络栈,快且可控,但绕过了中间件和 net/http 的实际分发逻辑。
立即学习“go语言免费学习笔记(深入)”;
容易踩的坑是忘记设置 Request.Body 或没重置 ContentLength,导致 json.Decode 读不到数据。
- 用
strings.NewReader构造请求体后,必须显式设置req.Body和req.ContentLength -
httptest.NewRecorder()返回的*httptest.ResponseRecorder不会自动写 header,需用recorder.Code和recorder.Body.String()检查结果 - 若 handler 内部调用了
r.Context().Value(),需提前用context.WithValue构建带值的 request
func TestCreateUser_BadJSON(t *testing.T) {
req := httptest.NewRequest("POST", "/users", strings.NewReader("{invalid"))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
handler := http.HandlerFunc(CreateUser)
handler.ServeHTTP(w, req)
if w.Code != http.StatusBadRequest {
t.Errorf("expected 400, got %d", w.Code)
}
}
依赖注入:用接口隔离外部依赖,而不是结构体字段
Go 里“依赖注入”不是靠容器,而是靠函数参数或构造函数参数接收符合接口的实现。重点在于定义窄而专注的接口,比如 UserRepository,而不是暴露整个 *sql.DB。
很多人误以为要给每个 struct 字段都加 setter 方法,其实只要 handler 的构造函数能接收依赖,测试时就能传 mock。
- 避免在 handler 内部 new 数据库连接;把连接/客户端作为参数传入构造函数
- 接口定义越小越好,例如
type UserRepo interface { Create(*User) error },比type DB interface{ Exec, Query, Begin... }更易 mock - 不要为了测试而导出内部字段(如
DB *sql.DB),那等于把实现细节暴露给测试,破坏封装
测试中间件时,把 handler 当作可组合的函数链
Go 的中间件本质是 func(http.Handler) http.Handler,测试它的核心思路是:给它一个已知行为的 handler(比如总是返回 200 的 http.HandlerFunc),然后检查包装后的 handler 是否按预期修改了 request / response / status。
典型陷阱是只测中间件本身是否 panic,却没验证它对下游 handler 的调用是否发生、是否被拦截、是否修改了 context。
- 用
httptest.NewRecorder和空http.HandlerFunc捕获中间件是否调用了next.ServeHTTP - 若中间件往
context写值,构造 request 时用req = req.WithContext(context.WithValue(...)),再传给中间件包装后的 handler - JWT 验证类中间件,应单独测 token 解析失败路径,不要和业务 handler 混在一起
真正难的不是写测试,而是决定哪些逻辑该进 handler、哪些该抽成独立函数、哪些依赖该抽象成接口——这些决策一旦定型,测试就只是补全验证而已。接口定义太宽、mock 太重、handler 里混着数据库调用,都会让测试变得脆弱又难写。










