为了账号安全,请及时绑定邮箱和手机立即绑定

如何使用 golang 中的并行子测试处理父测试拆解

如何使用 golang 中的并行子测试处理父测试拆解

Go
qq_笑_17 2023-05-22 17:08:55
概述如果我有一个带有设置和拆卸逻辑的父测试,我如何在其中并行运行子测试而不会遇到带有拆卸逻辑的竞争条件?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 mainimport (    "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 = 2func 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")            }        })    }}
查看完整描述

2 回答

?
富国沪深

TA贡献1790条经验 获得超9个赞

如何执行此操作:

func TestParallelSubtest(t *testing.T) {

    // setup test variables

    fname := setup(t)


    t.Run("group", func(t *testing.T) {

        for i := 0; i < ncase; i++ {

            t.Run(fmt.Sprintf("test_%d", i), func(t *testing.T) {

                t.Parallel()

                if _, err := os.Stat(fname); os.IsNotExist(err) {

                    t.Fatalf("file was removed before subtest finished")

                }

            })

        }

    })

    

    os.Remove(fname)

}

博客文章的相关部分位于Control of Parallelism:


每个测试都与一个测试函数相关联。如果一个测试的测试函数在其testing.T. 并行测试永远不会与顺序测试同时运行,并且它的执行会暂停,直到它的调用测试函数(父测试的调用函数)返回为止。[...]


一个测试会阻塞,直到它的测试函数返回并且它的所有子测试都已经完成。这意味着由顺序测试运行的并行测试将在运行任何其他连续顺序测试之前完成。


您的问题的具体解决方案可以在参考资料部分找到Cleaning up after a group of parallel tests。


查看完整回答
反对 回复 2023-05-22
?
心有法竹

TA贡献1866条经验 获得超5个赞

从 Go 1.14 开始,testing.T有了允许注册拆卸回调的方法testing.BCleanup

t.Cleanup(func() {
    os.Remove(fname)
})


查看完整回答
反对 回复 2023-05-22
  • 2 回答
  • 0 关注
  • 122 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信