Go中的t.Parallel()使用教程


在Go编程语言(Golang)的上下文中,该t.Parallel()函数经常用于测试。在 Go 中编写测试时,该testing包提供了一种T类型,该类型具有用于管理和报告测试状态的方法。该t.Parallel()方法用于将测试或子测试标记为能够与其他测试并行运行。

这是一个简短的解释:

  • t.Parallel():将调用测试或子测试标记为能够与其他测试并行运行。当测试或子测试被标记为并行时,它可以与其他并行测试同时执行,从而可能缩短测试执行时间。

Go语言中的t.Parallel()函数可以将测试用例标记为并行执行,从而提高测试的速度。

  • 这个函数在处理大型测试套件时特别有用。然而,并不是所有的测试套件都适合并行执行,对于小型测试套件来说,可能会增加额外的开销。
  • 另外,使用t.Parallel()需要注意一些问题,比如数据库共享、事务死锁和日志输出。

package mypackage

import (
    "testing"
   
"time"
)

func TestSomething(t *testing.T) {
    t.Parallel()

   
// Test logic here
    time.Sleep(2 * time.Second)
    t.Log(
"TestSomething completed")
}

在上面的示例中,在函数t.Parallel()内调用TestSomething来指示该测试可以与其他并行测试同时运行。

使用之前:

$ go test ./server/api -count=1
ok      github.com/crunchydata/priv-all-platform/server/api     1.486s
$ go test ./server/api -count=10
ok      github.com/crunchydata/priv-all-platform/server/api     11.786s

使用之后:

$ go test ./server/api -count=1
ok      github.com/crunchydata/priv-all-platform/server/api     0.966s
$ go test ./server/api -count=10
ok      github.com/crunchydata/priv-all-platform/server/api     3.959s

这些测试已经相当快了

测试goroutine
我们的测试使用goleak来检测任何意外泄漏的goroutine,这是我推荐的做法,因为泄漏goroutine却没有意识到它很容易成为 Go 的顶级枪械之一。

以前,我们有一个模式,其中每个测试用例都会检查自身是否有Goroutine泄漏,但是添加t.Parallel()打破了该模式,因为并行运行的测试用例会将彼此的Goroutine检测为泄漏。

解决方法是使用 goleak 的内置TestMain 包装器:

func TestMain(m *testing.M) {
    goleak.VerifyTestMain(m)
}

泄漏的goroutine只能在包级别的粒度上检测到,但只要您从无泄漏的基线开始,就足以检测回归。

总的来说
使用t.Parallel()需要根据具体情况进行考虑:

  • Go语言的并行测试功能通过t.Parallel()可以让特定测试用例在同一个包内并行运行,提高测试速度。
  • 在大型包中使用t.Parallel()可以显著减少测试时间,对于频繁运行测试的包来说,速度提升对于开发体验非常重要。
  • 添加t.Parallel()可以保持测试套件的快速运行,尤其是在包变得庞大时,而且在测试用例中一开始就加入t.Parallel()比后期添加更容易。
  • 在测试中需要“t.Parallel()”,但在子测试中不需要

普遍使用 . 并不完全符合 Go 约定t.Parallel()。也就是说,它减少了我们对大型包的测试迭代时间 30-40%,这足以是一个开发胜利,我个人打算将它用于未来的 Go 项目。
尽管提高测试速度是其主要优点,但与go test . -race它结合使用时,实际上可以帮助解决一些棘手的并行安全错误,而这些错误仅通过顺序测试运行无法捕获。这是一个很大的优势,因为整个类别的错误在生产中很难调试。
激活t.Parallel()现有项目的所有地方可能是一件大事,但从一开始就将其集成,持续成本非常低,并且可能会在以后产生巨大的好处。