0.0%覆盖率表示测试未执行而非未覆盖,需检查:_test.go命名、Test开头函数、在目标包目录运行、go test -v确认测试被识别。

go test -cover 显示 0.0% 是测试没跑,不是没覆盖
这是 Gin 项目里最常被误判的问题。输出 coverage: 0.0% of statements 不代表你写得差,而是测试压根没被识别或执行。
检查这四点:
-
_test.go文件名必须严格以_test.go结尾(handler_test.go✅,test_handler.go❌) - 测试函数必须以
Test开头,且签名是func(t *testing.T)(TestLoginHandler✅,checkLogin❌) - 确保在目标包目录下运行命令,比如
cd internal/auth && go test -cover;根目录下go test -cover默认不递归子包 - 先跑
go test -v,确认输出里有=== RUN TestXXX;没有就说明测试函数根本没进扫描范围
HTTP handler 的 error 分支和 context 超时路径永远不覆盖?手动构造
Gin handler 里的 if err != nil、select { case 这类逻辑,在自然请求中极难触发——你不可能靠发个 POST 就让 JSON 解析失败,也不可能等 1ms 就超时。
必须主动模拟异常场景:
- 用
httptest.NewRequest("POST", "/login", nil)构造空 body,触发c.ShouldBindJSON(&req)报错 - 构造已 cancel 的 ctx:
ctx, cancel := context.WithCancel(context.Background()); cancel(),再传给 handler(比如通过gin.CreateTestContext注入) - 对下游依赖(DB、cache、HTTP client)用
gomock或testify/mock注入两次行为:一次返回nil,一次返回errors.New("timeout") - 别忽略
defer resp.Body.Close()这类清理逻辑——提前 return 会导致它不执行;可临时改成if err := resp.Body.Close(); err != nil { t.Log(err) }验证是否遗漏
覆盖率数字没用,要看 go tool cover -func 定位具体函数
go test -cover 输出的全局百分比(如 coverage: 68.2%)对调试毫无价值。几十个包混在一起,你根本不知道是哪个 ValidateToken 的 else 分支漏了。
真正该做的是生成计数 profile 并查函数级明细:
- 在目标包下执行:
go test -covermode=count -coverprofile=coverage.out - 然后运行:
go tool cover -func=coverage.out - 重点关注输出中带
0.0%、33.3%、50.0%的行——这些数字直接对应分支未覆盖数(比如 if/else 二选一,只测了 if 就是 50.0%) - 配合
go tool cover -html=coverage.out看高亮 HTML 报告,红色行就是待补测试的硬伤
中间件和依赖解耦是覆盖率提升的前提
一个绑死 DB 实例、硬编码日志器、直接调用 http.Get 的 handler,几乎无法写出完整分支覆盖的测试——你连 mock 的入口都找不到。
重构方向很明确:
- 把外部依赖抽成接口,比如
type UserService interface { GetUser(id string) (User, error) },handler 只依赖这个接口 - 用构造函数参数或依赖注入容器(如
samber/do)传入依赖,而非在函数内直接 new - 中间件本身也要可测:把它当成普通函数,传入 mock 的
*gin.Context,检查它是否调用了c.Abort()或设置了c.Status(401) - 避免在 handler 里写长逻辑;超过 15 行就该拆——函数越小,单测越容易写全,覆盖率自然上去
覆盖率数字本身不重要,重要的是你是否真的验证过所有分支行为。尤其是 context.Cancel、io.EOF、database/sql.ErrNoRows 这些错误值,它们往往藏在 handler 最深的嵌套里,不主动构造就永远红着。











