gin接口测试用gin.createtestcontext而非http.servehttp,因其能复现中间件链、路由匹配和上下文初始化;需手动设header、params等;sqlmock适配dao层,gomock适配service层;中间件可单元测状态变更或集成测行为影响;json响应优先用assert.jsoneq,强约束场景用struct解码断言。

为什么 Gin 接口测试不用 http.ServeHTTP 而要用 gin.CreateTestContext
因为直接调用 http.ServeHTTP 会绕过 Gin 的中间件链、路由匹配和上下文初始化逻辑,导致 c.ShouldBind、c.Param、c.GetHeader 等行为返回空或 panic。Gin 官方推荐用 gin.CreateTestContext + httptest.NewRequest 搭配,它能复现真实请求生命周期。
实操建议:
- 始终用
gin.CreateTestContext创建c,再用c.Request = req注入测试请求 - 手动设置
req.Header.Set("Content-Type", "application/json"),否则c.ShouldBindJSON会静默失败 - 若测试带路径参数的接口(如
/users/:id),必须调用c.Params = gin.Params{{Key: "id", Value: "123"}},否则c.Param("id")返回空字符串
Mock 数据库依赖时,sqlmock 和 gomock 怎么选
sqlmock 专用于拦截 *sql.DB 调用,适合测试 DAO 层;gomock 生成 interface mock,适合隔离 service 层对 repository 的依赖。两者不冲突,常组合使用:service 层用 gomock 模拟 repository 接口,DAO 层用 sqlmock 验证 SQL 执行细节。
容易踩的坑:
立即学习“go语言免费学习笔记(深入)”;
- 用
gomock时,repository 接口必须定义在被测模块外(比如repo.UserRepo),否则mockgen无法生成 -
sqlmock默认严格模式,未预期的查询会 panic,调试时可临时加sqlmock.New(sqlmock.QueryMatcherEqual)改为宽松匹配 - 别在 test 文件里用
defer db.Close()——sqlmock的db是假的,Close 会报错
Gin 中间件怎么单独测,又怎么集成到 handler 测试里
中间件本质是 func(*gin.Context),可直接传入 mock context 调用;集成测试时则需把中间件注册进 gin.Engine,再走完整请求流程。关键区别在于:单元测中间件只关心它是否改写了 c.Next() 前后的状态(如 c.Abort()、c.Set());集成测则验证它是否影响下游 handler 的行为(如鉴权失败是否返回 401)。
实操要点:
- 测中间件本身:用
gin.CreateTestContext创建c,手动设c.Request.Header.Set("X-Auth", "valid"),然后调用yourMiddleware(c),检查c.IsAborted()或c.Get("user_id") - 集成测带中间件的路由:用
engine := gin.New(),engine.Use(yourMiddleware),再engine.POST("/api/foo", handler),最后用httptest.NewRecorder发请求 - 注意中间件顺序:Gin 不校验重复注册,但
Recovery()必须在最外层,否则 panic 会穿透出去
测试 JSON 响应结构时,assert.JSONEq 和手动 json.Unmarshal 各有什么代价
assert.JSONEq(来自 testify/assert)忽略字段顺序、空白符和类型差异(如 "123" 和 123 视为相等),适合快速比对响应体;手动 json.Unmarshal 到 struct 再逐字段断言,能精确控制字段存在性、类型、嵌套结构,但写起来啰嗦,且易因 struct 字段 tag 错误(如漏写 json:"foo")导致解码失败。
建议场景:
- 接口契约稳定、只关心字段值:用
assert.JSONEq,一行搞定 - 要验证错误响应中
error.code是否为整数、或data字段是否为 null:必须用 struct 解码,因为JSONEq把null和{}当成等价 - 避免用
bytes.Contains检查响应字符串 —— 缺少 JSON 格式校验,字段名拼错也测不出来
真实项目里,多数 handler 测试用 JSONEq 足够,但涉及 OpenAPI 规范强约束的接口(比如支付回调),得退回到 struct 解码+字段级 assert。










