正如package testing中记载的那样,在测试函数或子测试函数之外的任何 goroutine 中调用 t.FailNow
,t.Fatal
等都是不正确的。如果你的测试启动了新的 goroutine,它们不能从这些 goroutine 内部调用这些函数。
测试辅助函数通常不会从新的 goroutine 发出失败信号,因此它们使用t.Fatal
是完全正确的。如果有疑问,可以调用 t.Error 并返回。
// Good:
func TestRevEngine(t *testing.T) {
engine, err := Start()
if err != nil {
t.Fatalf("Engine failed to start: %v", err)
}
num := 11
var wg sync.WaitGroup
wg.Add(num)
for i := 0; i < num; i++ {
go func() {
defer wg.Done()
if err := engine.Vroom(); err != nil {
// This cannot be t.Fatalf.
t.Errorf("No vroom left on engine: %v", err)
return
}
if rpm := engine.Tachometer(); rpm > 1e6 {
t.Errorf("Inconceivable engine rate: %d", rpm)
}
}()
}
wg.Wait()
if seen := engine.NumVrooms(); seen != num {
t.Errorf("engine.NumVrooms() = %d, want %d", seen, num)
}
}
在测试或子测试中添加t.Parallel
并不会使调用t.Fatal
变得不安全。
当所有对 testing
API 的调用都在 test function 中时,通常很容易发现不正确的用法,因为go
关键字是显而易见的。传递testing.T
参数会使跟踪这种用法更加困难。通常,传递这些参数的原因是为了引入一个测试辅助函数,而这些测试辅助函数不应该依赖于被测系统。因此,如果一个测试辅助函数注册了一个致命的测试失败,它可以而且应该从测试的 goroutine 中这样做。