sqlmock 报“no query expected”根本原因是 handler 未使用 mock db,而是连接真实数据库或执行未注册 sql;常见于依赖未注入、硬编码初始化或全局 db 变量。

Mock数据库调用时,为什么 sqlmock 会报 there is no query expected
根本原因是 Gin handler 中的数据库操作没走 mock 驱动,而是连了真实 DB 或用了未注册的语句。常见于:没把 mock DB 实例注入到 handler 依赖中、用 gorm.Open("sqlite", ...) 这类硬编码初始化、或 handler 直接 new 了全局 DB 变量。
实操建议:
- 确保 handler 接收的是接口(如
DBInterface),测试时传入sqlmock.New()返回的 mock 对象 - 禁用所有全局 DB 变量;改用依赖注入(例如通过
func NewHandler(db DBInterface) *Handler) - mock 注册语句必须与实际执行的 SQL 完全一致——包括空格、换行、参数占位符(
?vs$1) - 若用 GORM,需搭配
gorm.WithContext(...).Debug().Exec(...)触发真实 SQL 执行,否则sqlmock不会拦截
Gin 测试中如何让 c.Request.Body 可重复读
默认 c.Request.Body 是单次读取流,mock 请求后第二次解析(比如 bind JSON + 手动读 body)会失败,报 http: read on closed body 或空数据。
实操建议:
- 用
bytes.NewReader构造请求体,并在每次测试前重置:body := strings.NewReader(`{"id":1}`)<br>req, _ := http.NewRequest("POST", "/user", body)<br>req.Header.Set("Content-Type", "application/json") - 避免在 handler 内多次调用
c.Request.Body.Read();统一用c.ShouldBindJSON(&v)解析,它内部已处理缓冲 - 若必须手动读 body,先调用
c.Request.Body = io.NopCloser(bytes.NewReader(buf))复制一份
用 testify/mock 模拟仓储层接口时,方法签名不匹配导致 panic
典型错误是 mock 生成的代码方法签名和实际接口不一致,比如返回值顺序错、指针 vs 值类型、error 位置不对,运行时直接 panic “method not found”。
实操建议:
- 用
mockgen生成 mock 时指定完整路径:mockgen -source=repository/user.go -destination=mocks/mock_user.go -package=mocks
- 检查原始接口是否含未导出方法(mockgen 会跳过);确保所有方法首字母大写
- 生成后立刻
go fmt,防止因格式问题导致编译器误判签名 - handler 测试中传入 mock 实例前,用
mockCtrl := gomock.NewController(t)管理期望,别漏掉defer mockCtrl.Finish()
Gin handler 单元测试里怎么验证 JSON 响应结构和状态码
只断言 status == 200 不够,容易漏掉字段缺失、类型错误或嵌套结构异常;而全量 JSON 字符串比对又脆弱,一加字段就挂。
实操建议:
- 用
httptest.NewRecorder()捕获响应,再用json.Unmarshal(recorder.Body.Bytes(), &resp)解析到结构体 - 定义精简的响应结构体(如
type UserResp struct { ID int `json:"id"` Name string `json:"name"` }),只包含要校验的字段 - 状态码用
assert.Equal(t, http.StatusOK, recorder.Code),字段用assert.Equal(t, 123, resp.ID) - 避免用
assert.JSONEq做模糊匹配——它不校验字段缺失,且对浮点数精度敏感











