在 goroutine 中调用 t.error 无效,必须通过 chan error 将错误传回主 goroutine 统一上报,避免竞态和死锁,且需及时关闭 channel。

test: 如何让 t.Error 在 goroutine 里生效
Go 测试中直接在 goroutine 里调用 t.Error 是无效的——t 不是线程安全的,且测试主 goroutine 结束后,子 goroutine 的错误输出会被丢弃或静默忽略。
真正能用的方式只有一种:把错误传回主 goroutine,由它统一上报。别指望子 goroutine 自己打日志或调用 t.Errorf。
- 用
chan error接收子 goroutine 的错误(最常用、最可控) - 避免用
sync.WaitGroup+ 全局变量收集错误——竞态难排查,且t不能跨 goroutine 调用 - 如果 goroutine 必须提前退出(比如超时),记得关闭 error channel 防止主 goroutine 死锁
errCh := make(chan error, 1)
go func() {
defer close(errCh)
if err := doSomething(); err != nil {
errCh <- err
return
}
}()
select {
case err := <-errCh:
if err != nil {
t.Errorf("goroutine failed: %v", err)
}
case <-time.After(2 * time.Second):
t.Fatal("timeout waiting for goroutine")
}testing.T: 为什么 t.Parallel() 和 goroutine 错误捕获不能混用
加了 t.Parallel() 的测试函数,其生命周期由 testing 包调度管理;一旦主 goroutine 返回,整个测试即视为结束——此时还在跑的子 goroutine,即使往 channel 发送错误,也可能被丢弃(channel 已无人接收)。
- 只要用了
t.Parallel(),所有并发逻辑必须在主 goroutine 返回前完成同步(包括 error channel 的读取) - 不要在
t.Parallel()测试里启动“后台型” goroutine(比如监听、轮询),这类行为应移出测试或改用显式等待 - 若需并行多个带错误的 goroutine,每个都配独立
errCh+select,且全部在 return 前处理完
sync.WaitGroup + context.Context: 怎么安全等 goroutine 并捕获错误
单纯靠 WaitGroup 只能等结束,无法区分成功/失败;加上 context.Context 可以统一控制取消,但错误仍得靠外部 channel 或返回值传递。
立即学习“go语言免费学习笔记(深入)”;
- 推荐组合:
sync.WaitGroup控制生命周期 +chan error传错 +context.WithTimeout防卡死 - 别在 goroutine 里调用
ctx.Done()后还往 error channel 写——要先判断ctx.Err() != nil再决定是否继续 - error channel 容量至少为 1,否则第一个错误就可能阻塞 goroutine(尤其多个 goroutine 共用一个 channel 时)
gotest: 运行时看到 panic: test executed panic on zero Context 是什么情况
这不是 Go 标准库报的错,而是某些第三方测试辅助库(比如 github.com/stretchr/testify 的旧版 mock 或自定义断言)在 goroutine 中误用了未初始化的 *testing.T 实例导致的。根本原因还是试图跨 goroutine 使用 t。
- 检查是否在 goroutine 中直接传入了
t(例如go someFunc(t)),这是绝对禁止的 - 确认没用
testify/mock的mock.TestingT构造假t并在 goroutine 中调用其方法 - 最稳妥的做法:任何测试逻辑,只要涉及并发,就只传原始参数、context、channel,不传
*testing.T
测试里并发错误捕获的关键,从来不是“怎么起 goroutine”,而是“怎么让错误不掉进黑洞”。channel 容量、context 取消时机、t.Parallel() 的作用域边界——这些地方一松动,错误就消失得无影无踪。










