用 go test 测并发需手动启 goroutine 并用 sync.WaitGroup 等待完成,避免 time.Sleep;t.Parallel() 仅让不同 TestXxx 函数并行,不控制函数内并发;正确做法是构造可验证副作用、显式同步、隔离共享状态,并配合 -race 检测竞态。
go test 跑并发场景?直接用 go test 本身不支持“启动 N 个 goroutine 并发执行测试逻辑”,它默认是串行跑每个 TestXxx 函数。真要测并发行为,得自己在测试函数里手动启 goroutine,并配合适当的同步机制。
常见错误是写完 go f() 就结束测试函数,结果主 goroutine 退出、子 goroutine 被强制终止,测试看似“通过”实则没跑完。
sync.WaitGroup 或 chan 等方式等所有并发任务完成time.Sleep 等待——它不可靠,CI 环境负载高时容易误判sync.Mutex 或改用 sync/atomic
testing.T.Parallel() 是干啥的?能测并发逻辑吗?不能。testing.T.Parallel() 的作用只是让多个 TestXxx 函数**彼此并行运行**(比如 TestLogin 和 TestLogout 同时跑),和你代码里是否用了 goroutine 完全无关。它不提供任何并发控制能力,也不影响单个测试函数内部的行为。
典型误用:在 TestConcurrentUpdate 里调了 t.Parallel(),然后以为这样就能“测出竞态”——其实只是让这个测试和其他测试并行跑,自己内部还是串行执行。
t.Parallel() 的兄弟测试会跳过(因为它们不是同一层级)go test -race,而不是依赖 t.Parallel()

核心是:构造可验证的并发副作用 + 显式等待 + 隔离干扰。比如测试一个并发安全的计数器:
func TestCounter_ConcurrentInc(t *testing.T) {
var c Counter
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < 100; j++ {
c.Inc()
}
}()
}
wg.Wait()
if got := c.Load(); got != 10000 {
t.Errorf("expected 10000, got %d", got)
}
}
注意几个关键点:
i(上面示例没犯这个错,但新手常犯)c)要是测试函数内定义的,避免和其他测试共享状态go test -race 运行,才能捕获实际的读写冲突;光靠断言值对不对,可能掩盖竞态httptest.Server 或临时目录隔离,否则并发下容易端口冲突或文件覆盖本质是并发测试对环境更敏感。最常见原因有三个:
t.Parallel() 时更容易暴露)解决方向很明确:所有测试必须自治。端口用 0 让系统分配,文件用 os.MkdirTemp,数据库用内存实例或每次清库。别省这几行代码,不然花半天 debug CI 失败不值得。