go test -cover 无法反映错误路径、边界输入和并发异常的覆盖情况,需通过构造特定错误、表格驱动边界测试、主动模拟竞态及重构不可测代码来实质提升覆盖率。

go test -cover 只能告诉你“哪行没跑过”,但真正决定覆盖率能否实质提升的,是测试是否触达了错误路径、边界输入和并发异常——这些恰恰是线上故障最常发生的场景。
补全错误处理路径:别只测 nil,要测「错得刚好」
很多函数看似逻辑简单,却因错误传播不完整导致覆盖率卡在 80% 上下。比如一个封装 HTTP 调用的 DoRequest 函数,若只测服务返回 200,那 io.EOF、net/http.ErrServerClosed、超时触发的 context.DeadlineExceeded 全部裸奔。
- 在测试中显式构造底层错误并验证上层是否透传或包装:
mockClient.DoFunc = func(*http.Request) (*http.Response, error) { return nil, context.DeadlineExceeded } - 避免只断言
err != nil,改用errors.Is(err, context.DeadlineExceeded)或errors.As确保错误类型和语义一致 - 对自定义错误(如
mcp/errors.go中的ResourceNotFoundError),必须覆盖从生成 → 包装 → 检查的整条链
用表格驱动测边界:空、零、超长、嵌套、转义
覆盖率低的代码,90% 集中在条件分支和循环入口。人工写一堆TestXxxWithEmptyInput 效率低还易漏,表格驱动是 Go 社区验证边界的事实标准。
- 边界值不是“随便试试”,而是按类型系统化枚举:
- 字符串:
""、"\x00"、"\u0000"、strings.Repeat("a", 65536) - 数字:
0、math.MaxInt64、math.SmallestNonzeroFloat64 - JSON 结构:
"{\"\":"\""}"(键为空字符串)、"[{}{}{}]"(无分隔逗号)、"{\"a\":null}"(null值)
- 字符串:
- 示例中
ParseResourceURI的测试直接列出""、"ftp:///test.txt"、1024 个 a,比写三个独立函数更易维护和扩展
拆解并发与 race 场景:别信 -race 不报错就安全
go test -race 能发现数据竞争,但无法保证逻辑正确性。像连接池回收、会话续租、计数器递增这类操作,即使没 race,也可能因执行顺序不同导致状态错乱。
- 单元测试里主动制造竞态:用
sync.WaitGroup控制 goroutine 启动时机,验证共享状态是否符合预期 - 对带锁逻辑(如
sync.RWMutex),单独写TestConcurrentReadAndWrite,而非混在功能测试里 - 注意
-covermode=atomic是并发安全的覆盖率模式,普通count模式在多 goroutine 下统计会失真——nats.go 和 mcp-go 都强制使用它
覆盖率报告别只看百分比:盯住红色行背后的「为什么不可测」
HTML 报告里标红的代码行,有些是真遗漏,有些则是设计缺陷信号。比如:- 一行
if err != nil { log.Fatal(err) }永远红?说明这不该是单元测试该覆盖的路径,而应重构为可返回、可 mock 的错误处理 -
time.Now()或rand.Intn()导致整段逻辑无法稳定触发?应提取为接口参数,测试时注入固定时间/随机源 -
os.Exit(1)类调用让测试中断?改用返回错误 + 外层处理,把退出逻辑提到 main 层
工具只是镜子,照出的是代码的可测性,而不是测试写得够不够多。










