go test -cover 默认仅显示整体覆盖率百分比,无法定位未覆盖的具体代码行;应先用 go test -coverprofile=coverage.out ./... 生成覆盖率文件,再通过 go tool cover -func=coverage.out 查看函数级覆盖率,重点关注0.0%等低覆盖值,并用 go tool cover -html=coverage.out 生成HTML报告精确定位未覆盖语句。

go test -cover 只显示百分比,根本看不出哪行没测到
默认执行 go test -cover 只返回一个笼统的数字,比如 coverage: 72.3% of statements,但完全不知道是哪个 if err != nil 分支被跳过、哪个 defer 没触发、哪段 context 取消逻辑压根没走。这数字看着还行,其实关键错误路径全裸奔。
真正该做的第一步是生成带明细的覆盖率文件:
- 运行
go test -coverprofile=coverage.out ./...(注意末尾的./...覆盖所有子包) - 立刻执行
go tool cover -func=coverage.out,它会按函数列出覆盖率,重点关注0.0%、33.3%、50.0%这类值——它们几乎总对应着未触发的else、error分支或defer块 - 再用
go tool cover -html=coverage.out -o coverage.html打开 HTML 报告,直接点击红色未覆盖行,定位到具体语句
表格驱动测试必须覆盖 error 分支,不能只 mock 成功路径
很多测试只写 mock.On("GetUser").Return(user, nil),结果 if err != nil 那行永远不执行。Go 的错误处理是显式的,你得手动造错:
- 对同一 mock 方法至少定义两个行为:
mock.On("GetUser").Return(nil, errors.New("db timeout")) - HTTP handler 测试里,用
httptest.NewRequest("GET", "/user/123", nil)是不够的,要加bytes.NewReader([]byte(""))模拟空 body 触发json.Decode失败 - 注意
nil切片和空切片区别:测试时既要传var s []string = nil,也要传s := []string{},二者在len(s) == 0后可能走不同分支
context 取消路径必须进表格,别用 time.Sleep 等超时
写 time.Sleep(2 * time.Second) 等 context 超时,既慢又不可靠,CI 上还容易 flaky。正确做法是把 context 构造本身纳入测试数据表:
立即学习“go语言免费学习笔记(深入)”;
- 在表格结构体里加字段:
ctx context.Context和cancel func() - 每个用例分别调用
context.WithTimeout或context.WithCancel,并在 case 开头就cancel() - 这样能精准触发
select { case 分支,且不依赖真实时间
defer 日志或资源清理没执行?说明函数提前 return 了
defer 行显示未覆盖,通常不是 defer 写错了,而是函数在 defer 之前就 return 了——比如某个 if 条件成立后直接返回,后面所有 defer 全被跳过。
解决方法很直接:
- 找到那个未覆盖的
defer所在函数 - 检查所有提前
return的位置,尤其是if err != nil { return }后面有没有漏掉的defer - 把资源清理逻辑拆出来单独成函数,再在所有出口点显式调用,比依赖 defer 更可控
覆盖率工具暴露的从来不是“要不要写测试”,而是“代码结构是否允许你干净地测试”。那些 0.0% 的函数,往往就是设计上没留出错误注入点或上下文控制入口的地方。


















