概述
如果我有一个带有设置和拆卸逻辑的父测试,我如何在其中并行运行子测试而不会遇到带有拆卸逻辑的竞争条件?
func TestFoo(t *testing.T) {
// setup logic
t.Run("a", func(t *testing.T) {
t.Parallel()
// test code
})
// teardown logic
}
例子
作为一个人为的例子:假设测试需要创建一个 tmp 文件,所有子测试都将使用该文件,并在测试结束时将其删除。
例如,父测试也调用t.Parallel(),因为这是我最终想要的。但是我的问题和下面的输出是一样的,即使父母不打电话t.Parallel()。
序贯子测验
如果我按顺序运行子测试,它们将毫无问题地通过:
package main
import (
"fmt"
"io/ioutil"
"os"
"testing"
)
func setup(t *testing.T) (tmpFile string) {
f, err := ioutil.TempFile("/tmp", "subtests")
if err != nil {
t.Fatalf("could not setup tmp file: %+v", err)
}
f.Close()
return f.Name()
}
var ncase = 2
func TestSeqSubtest(t *testing.T) {
t.Parallel()
// setup test variables
fname := setup(t)
// cleanup test variables
defer func() {
os.Remove(fname)
}()
for i := 0; i < ncase; i++ {
t.Run(fmt.Sprintf("test_%d", i), func(t *testing.T) {
if _, err := os.Stat(fname); os.IsNotExist(err) {
t.Fatalf("file was removed before subtest finished")
}
})
}
}
输出:
$ go test subtests
ok subtests 0.001s
平行分测验
但是,如果我并行运行子测试,那么父测试的拆卸逻辑最终会在子测试有机会运行之前被调用,从而使子测试无法正确运行。
这种行为虽然不幸,但符合“使用子测试和子基准” go 博客所说的内容:
如果测试函数在其 testing.T 实例上调用 Parallel 方法,则该测试称为并行测试。并行测试永远不会与顺序测试同时运行,并且它的执行会暂停,直到它的调用测试函数(父测试的调用函数)返回为止。
func TestParallelSubtest(t *testing.T) {
t.Parallel()
// setup test variables
fname := setup(t)
// cleanup test variables
defer func() {
os.Remove(fname)
}()
for i := 0; i < ncase; i++ {
t.Run(fmt.Sprintf("test_%d", i), func(t *testing.T) {
t.Parallel() // the change that breaks things
if _, err := os.Stat(fname); os.IsNotExist(err) {
t.Fatalf("file was removed before subtest finished")
}
})
}
}
富国沪深
心有法竹
相关分类