通八洲科技

如何在 Go 单元测试死锁时中止执行并查看日志输出

日期:2025-12-29 00:00 / 作者:心靈之曲

当 go 单元测试发生死锁导致卡住时,直接按 ctrl+c 无法捕获 `t.log()` 输出;使用 ctrl+\ 可触发运行时栈回溯,辅助定位阻塞点,但需配合 `-v` 标志和合理日志策略才能确保关键日志可见。

在 Go 中运行单元测试(如 go test -run TestFoo)时,若测试因 goroutine 死锁、channel 阻塞或 mutex 争用而挂起,常规中断信号(Ctrl+C)会强制终止进程,导致尚未刷新的 t.Log() 消息丢失——这是因为 testing.T 的日志默认缓冲且仅在测试结束(成功/失败/超时)时批量输出。

正确做法是使用 Ctrl+\(SIGQUIT)
该信号不会立即退出测试,而是由 Go 运行时捕获并打印当前所有 goroutine 的完整调用栈(包括阻塞位置),例如:

$ go test -v -run TestDeadlock
=== RUN   TestDeadlock
^\
SIGQUIT: quit
PC=0x109c6a1 m=0 sigcode=0

goroutine 19 [chan send]:
example.com/mypkg.TestDeadlock(0xc0000b4180)
    deadlock_test.go:12 +0x71
...

⚠️ 注意:

func TestDeadlock(t *testing.T) {
    t.Parallel() // 谨慎启用,可能掩盖问题
    t.Log("starting test...")

    ch := make(chan int)
    t.Log("about to send on channel...")
    ch <- 42 // ← 此处将永久阻塞(无接收者)
}

运行:

go test -v -timeout 5s -run TestDeadlock

超时后自动终止,并输出已记录的日志 + panic 信息,比手动中断更可控。

✅ 最佳实践总结: