Go测试新手常见错误有哪些_Go测试易错点总结

1次阅读

go测试失败多因测试写法错误:①append不赋值导致切片未更新;②goroutine未同步即结束;③忽略err掩盖真实错误;④全局变量污染测试状态。

Go测试新手常见错误有哪些_Go测试易错点总结

Go 测试失败,十有八九不是代码逻辑错了,而是测试写法本身埋了雷——尤其对新手,很多错误现象看起来像“随机失败”或“结果不对”,实则是底层机制没吃透。


忘记 append 返回值导致切片修改不生效

测试中常要构造输入数据,比如用 append 动态加元素,但直接调用不赋值,切片内容根本没变:

func TestProcessItems(t *testing.T) {     items := []string{"a", "b"}     append(items, "c") // ❌ 错误:返回的新切片被丢弃     if len(items) != 3 {         t.Errorf("expected 3 items, got %d", len(items)) // 实际输出 2     } }
  • append 总是返回新切片(可能指向新底层数组),原变量不变
  • 切片是引用类型,但它的头信息(指针、长度、容量)是值传递
  • 常见于表驱动测试中动态构造测试数据,一漏就导致用例跑在空/旧数据上

✅ 正确写法:
items = append(items, "c")


并发测试里 goroutine 没等完就结束

go 启动协程做异步逻辑测试时,主测试函数退出后,goroutine 可能还在跑,导致:

  • 断言没执行到(静默跳过)
  • 数据竞争(go test -race 报告 data race)
  • 偶发 panic 或结果错乱
func TestAsyncUpdate(t *testing.T) {     var val int     go func() { val = 42 }() // ❌ 没同步机制,测试可能在赋值前就结束了     if val != 42 {         t.Error("val not updated")     } }
  • t.Parallel() 只控制测试函数并发,不管理内部 goroutine
  • 不要用 time.Sleep 硬等(不可靠、拖慢测试)

✅ 推荐做法:

  • sync.WaitGroup 显式等待
  • 或用带缓冲 channel + select 超时兜底
  • 更安全的模式:把异步逻辑封装成可注入的回调,测试中用同步实现替代

错误处理被忽略,err 永远是 nil

新手常写这样的测试:

func TestFetchData(t *testing.T) {     data, _ := FetchFromAPI() // ❌ 忽略 err,掩盖失败     if data == nil {         t.Fatal("data is nil")     } }

问题不止是“没检查错误”,更深层的是:

  • FetchFromAPI 若真出错(如网络超时),返回 nil + err,但 _ 吞掉错误,测试仍继续执行
  • 如果后续断言依赖 data 非空,就会 panic 或误判
  • 更隐蔽的是:有些函数在 error 时也返回部分有效数据,不检查 err 就无法区分成功/失败路径

✅ 必须显式检查:
if err != nil { t.Fatalf("FetchFromAPI failed: %v", err) }
或者用表驱动测试覆盖 hasError: true 场景


全局变量/包级状态污染多个测试

比如有个包级计数器:

var requestCount int <p>func Increment() { requestCount++ }</p><p>func TestFirst(t *testing.T) { Increment() if requestCount != 1 { t.Error("first test failed") } }</p><p>func TestSecond(t *testing.T) { Increment() if requestCount != 1 { // ❌ 实际是 2,因为 TestFirst 已改过它 t.Error("second test failed") } }
  • Go 测试默认顺序执行,但一旦启用 t.Parallel(),顺序不可控
  • init() 函数、包变量、单例对象都可能成为共享状态源
  • 表现为:单个测试通过,一起跑就失败;或者 CI 上偶发失败

✅ 应对策略:

  • 所有测试前用 defer 清理(如重置计数器)
  • 把状态移到测试函数内,或用局部结构体封装
  • 避免在 init 中做副作用操作(如连接数据库、读配置)

最麻烦的其实是那种“只在特定环境触发”的状态污染——比如本地跑没问题,CI 用不同 GOOS 或缓存导致行为偏移,这时候连复现都费劲。

text=ZqhQzanResources