t.Parallel() 仅在显式调用且无状态干扰时才并发执行;加了不快或panic的根本原因是测试间隐式共享资源,如包级变量、全局log、未关闭server等。

t.Parallel() 不是“加了就快”,它只在多个测试函数都显式调用、且彼此无状态干扰时才真正并发执行。
为什么加了 t.Parallel() 却没变快,甚至 panic?
根本原因不是并发没触发,而是测试间偷偷共享了资源。Go 测试运行器只负责把 t.Parallel() 标记的测试扔进不同 goroutine,不帮你隔离任何东西。
- 常见错误现象:
fatal error: concurrent map writes、数据库主键冲突、t.Log输出交错、某次测试成功、下次失败 - 典型隐式共享:包级变量(如
var db *sql.DB)、硬编码临时路径("./tmp")、全局log.SetOutput、未关闭的http.Server - 子测试里忘了加
t.Parallel():父测试调了,但t.Run("case1", ...)里的逻辑仍串行,且会因循环变量捕获错误导致数据错乱 - 用了
time.Sleep()或os.Setenv():前者让调度不可控,后者直接被 Go 运行时静默禁用并行
t.Parallel() 和 go test -p 的关系必须分清
t.Parallel() 是测试函数级开关,-p 是进程级调度参数——两者缺一不可,但作用域完全不同。
-
go test -p=1:哪怕所有测试都写了t.Parallel(),也强制串行;-p=0会报错 -
go test -p=4但没写t.Parallel():仍是逐个跑 TestA、TestB……完全不并发 - 真实并发数 = min(
-p值, CPU 核心数, 并行测试函数数量),不是你设多少就跑多少 - 推荐 CI 场景:代码里加
t.Parallel(),命令行用go test -p=2 -race,既控资源又抓竞态
哪些测试能加,哪些绝对不能加?
判断标准就一条:该测试是否「不依赖外部可变状态、不修改全局环境、自身幂等」。
立即学习“go语言免费学习笔记(深入)”;
- 适合加:
TestParseJSON、TestValidateEmail、TestCalculateTax—— 纯计算、无 IO、输入输出确定 - 需谨慎加:
TestHandleHTTPRequest(必须用httptest.NewRequest+httptest.NewRecorder,禁用真实网络) - 绝对不能加:
TestWriteToFile(除非用t.TempDir())、TestMigrateDB(哪怕用:memory:,也要注意 SQLite 连接模式)、TestSetFlag(flag.Parse()非并发安全) - mock 后的 DB/Redis/HTTP 测试可以加,前提是 mock 实例不复用(每个测试 new 一个 mock.Client)
正确写法和最容易漏掉的细节
最常被忽略的不是“怎么写”,而是“在哪写”和“写完之后要做什么”。
-
t.Parallel()必须放在测试函数第一行,早于t.Log、t.Run、任何 setup 逻辑,否则被忽略 - 子测试中也要单独调用:
t.Run("valid", func(t *testing.T) { t.Parallel(); ... }),否则不并发 - 循环中启动子测试时,必须显式捕获循环变量:
item := item,否则所有子测试看到的是最后一个值 - 涉及 goroutine 的测试,别用
time.Sleep等待,改用sync.WaitGroup或带缓冲的chan error - 每次运行前务必加
-race:不跑go test -race就开t.Parallel(),等于裸奔


















