go test -cover 默认不输出数字,需加-v才显示覆盖率百分比;要生成行级报告须用-coverprofile生成文件再用go tool cover解析。

go test -cover 为什么没输出数字?
默认 go test -cover 不会打印任何覆盖率数值,它只在测试执行时收集数据,但不主动输出——除非你加了 -v。不加 -v 就像默默干活却不说话,你根本不知道它干了没。
正确做法是:先确认当前目录有 *_test.go 文件且含至少一个 Test* 函数,再运行:
go test -v -cover
你会看到类似 coverage: 65.2% of statements 的输出。如果没输出、也没报错,大概率是没测试文件,或者测试函数名拼错了(比如写成 testAdd 而不是 TestAdd)。
-
-cover单独用 = 静默收集,不显示结果 -
-v是开关,打开它才能让覆盖率数字浮出来 - 没
Test*函数时,-cover完全不触发,也不报错
怎么生成能定位到具体行的覆盖率报告?
go test -cover 输出的百分比只是个模糊指标,真正有用的是知道哪一行没跑过。必须分两步走:先生成 profile 文件,再用 go tool cover 解析。
立即学习“go语言免费学习笔记(深入)”;
在项目根目录(即 go.mod 所在目录)运行:
go test -coverprofile=coverage.out -covermode=count ./
接着立刻执行:
go tool cover -html=coverage.out -o coverage.html
用浏览器打开 coverage.html,红色行就是未覆盖语句。注意三点:
- 必须在
go.mod目录下运行,否则 HTML 点击文件名会跳转失败(路径映射错乱) -
-covermode=count比atomic更适合本地调试:快、输出准、不和-race冲突 -
coverage.out是二进制,删了就得重跑测试,没法从 HTML 反向生成
跨包测试时覆盖率总漏掉 internal 或 model?
go test -cover 默认只统计当前包,哪怕你的 main 包 import 了 internal/utils,后者也不会被计入——除非你显式告诉它“把这些也带上”。
用 -coverpkg 列出所有要覆盖的包路径,注意格式:
- 路径必须是 module path(如
github.com/your/repo/internal/utils),不是./internal/utils - 多个包用英文逗号分隔,**不能有空格**:
-coverpkg=github.com/your/repo,github.com/your/repo/internal/utils - 先跑
go list ./...看实际子包列表,避免手写路径出错 - vendor 目录不会被自动包含,也不建议测 vendor 里的第三方代码
完整命令示例:
go test -coverprofile=coverage.out -covermode=count -coverpkg=github.com/your/repo,github.com/your/repo/internal,github.com/your/repo/model ./...
为什么 error 分支总显示“已覆盖”,但实际没走?
很多测试只 mock 成功路径,比如 mock.On("GetUser").Return(user, nil),结果 if err != nil 分支永远不执行,却仍被算作“覆盖”——因为 Go 的覆盖率统计只看语句是否被执行,不管分支逻辑有没有真正触发。
解决方法只有一个:把错误路径写进测试数据表里。
- 对同一接口方法,至少定义两个行为:
mock.On("GetUser").Return(nil, errors.New("timeout")) - HTTP handler 测试中,别只用
httptest.NewRequest("GET", "/user/123", nil),要加bytes.NewReader([]byte(""))触发json.Decode失败 - 区分
nil切片和空切片:var s []string = nil和s := []string{}在len(s) == 0后可能走不同分支,两者都得测 - context 取消路径别用
time.Sleep(2 * time.Second)等超时,应把ctx, cancel := context.WithTimeout(...)构造本身放进测试表,然后手动调cancel()
覆盖率数字高,不代表错误处理被验证过;真正难测的,从来不是 happy path。


















