加了 t.Parallel() 仍是串行执行,因 Go 测试框架默认串行,t.Parallel() 仅声明可并行,实际并发需满足:多个测试均声明、-p 参数允许、无共享状态竞争,且须在测试开头调用。

可以,但必须显式调用 t.Parallel(),且并行行为受测试函数生命周期和资源竞争约束。
为什么加了 t.Parallel() 还是串行执行?
Go 测试框架默认所有测试函数串行运行。即使写了 t.Parallel(),它只起到“声明本测试可并行”的作用,实际是否并行取决于:当前测试是否在另一个已调用 t.Parallel() 的测试之后立即启动、是否被 go test -p 限制、以及是否与同组测试共享状态(如全局变量或未加锁的文件句柄)。
-
t.Parallel()必须在测试函数开头尽早调用,否则 panic - 同一
testing.T实例不能既调t.Parallel()又调t.Run()子测试(子测试需单独决定是否并行) - 若父测试未设并行,其内部所有
t.Run()子测试也默认串行,除非子测试自己调t.Parallel()
t.Parallel() 的实际调度表现
Go 运行时按测试名分组调度,并发数由 go test -p=N 控制(默认为 CPU 核心数)。但真正并发执行的前提是:多个测试都声明了 t.Parallel(),且彼此不阻塞(比如没共用 sync.Mutex 或临时文件路径)。
func TestFetchData(t *testing.T) {
t.Parallel() // 必须放在第一行
resp, err := http.Get("https://httpbin.org/delay/1")
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
}
- 多个类似
TestFetchData函数会并发发起 HTTP 请求 - 但若它们都写入同一个本地文件(如
"output.json"),就会产生竞态或覆盖,这不是 Go 测试框架的问题,而是代码逻辑问题 - 可通过
t.TempDir()或t.Name()构造隔离路径避免
并行测试中常见的资源冲突点
最易被忽略的是隐式共享状态:环境变量、全局配置、数据库连接池、日志输出目标、甚至 time.Now() 的密集调用(影响基于时间的断言)。
- 修改
os.Setenv()后未恢复 → 影响其他并行测试 - 使用单例 DB 实例且未清空表 → 测试间数据污染
- 子测试里用
t.Run("xxx", ...)但闭包捕获了循环变量 → 所有子测试看到同一个值 - 并发写同一
*log.Logger(非线程安全)→ 日志错乱或 panic
解决方法不是禁用并行,而是让每个测试独占资源:用 t.Cleanup() 恢环境变量、用内存数据库(如 sqlite.Open(":memory:"))、用 sync.Pool 管理临时对象。
并行测试真正的门槛不在语法,而在能否识别和隔离所有跨测试的隐式依赖 —— 很多失败不是因为 t.Parallel() 不工作,而是测试本身就没做到独立。










