什么对 t.Cleanup 有用?

Tsu*_*iro 9 testing unit-testing go go1.14

问题

我想知道t.CleanupGo1.14中引入的用例。与使用 defer 相比,t.Cleanup 有什么方便之处?

https://golang.org/pkg/testing/#T.Cleanup

  • 样本

例如,假设我们创建了一个临时目录,当我们测试它时,我们想删除我们创建的临时目录。

t.Cleanup可用于编写如下测试,但它也可用作defer os.RemoveAll(tempDir).

package mypkg

import (
    "io/ioutil"
    "os"
    "testing"
)

func TestDirwalk(t *testing.T) {
    tempDir, err := ioutil.TempDir(".", "temp")
    if err != nil {
        t.Errorf("create tempDir: %v", err)
    }
    t.Cleanup(func() { os.RemoveAll(tempDir) })

    // something...
}
Run Code Online (Sandbox Code Playgroud)

icz*_*cza 16

如果您的测试出现恐慌,也会调用清理函数,因此在您的情况下两者都可以使用。

T.Cleanup()如果您的测试调用其他函数,使用的优势就很明显了testing.T。显然defer,这些函数中的using会在这些函数返回之前执行,但是如果您使用 注册清理函数T.Cleanup(),那么它们只会在测试结束时被调用。

可以将其T.Cleanup()视为defer. 它还记录了传递的函数用于清理目的。

  • 显然,在这些函数中使用 defer 会在这些函数返回之前执行,但是如果您使用 T.Cleanup() 注册清理函数,那么它们只会在测试结束时被调用。_ 我终于明白了。谢谢。 (2认同)

小智 7

除了其他人指出的之外,t.Cleanup()在处理并行子测试时也很有用,其中清理应该仅在所有子测试完成后运行。考虑

func TestSomething(t *testing.T){
   setup()
   defer cleanup()
   t.Run("parallel subtest 1", func(t *testing.T){
      t.Parallel()
      (...)
   })
   t.Run("parallel subtest 2", func(t *testing.T){
      t.Parallel()
      (...)
   })
}
Run Code Online (Sandbox Code Playgroud)

这是行不通的,因为测试函数将在子测试仍在运行时返回,导致子测试所需的资源被defer cleanup().

之前t.Cleanup()解决这个问题的方法是将子测试包装在另一个测试中

func TestSomething(t *testing.T){
   setup()
   defer cleanup()
   t.Run("parallel tests", func(t *testing.T){
      t.Run("subtest 1", func(t *testing.T){
         t.Parallel()
         (...)
      })
      t.Run("subtest 2", func(t *testing.T){
         t.Parallel()
         (...)
      })
   })
}
Run Code Online (Sandbox Code Playgroud)

看起来不错,但有了t.Cleanup()它会变得更好

func TestSomething(t *testing.T){
   setup()
   t.Cleanup(cleanup)
   t.Run("parallel subtest 1", func(t *testing.T){
      t.Parallel()
      (...)
   })
   t.Run("parallel subtest 2", func(t *testing.T){
      t.Parallel()
      (...)
   })
}
Run Code Online (Sandbox Code Playgroud)


J. *_*der 5

t.Cleanup 当测试不关心资源本身时,可用于清理辅助函数分配的资源。

例子

考虑测试服务层。该服务使用 a*sql.DB但不创建它本身。

package testutils

import (
  "testing"

  "my/db"
  "my/domain"
)

func NewTestSubject(t *testing.T) *domain.Service {
  t.Helper()  
  sqldb := newDatabase(t)
  s, _ := domain.NewService(sqldb)
  return s
}

func newDatabase(t *testing.T) *sql.DB {
  t.Helper()
  d, _ := db.Create()
  t.Cleanup(func() {
    d.Close()
  })
}
Run Code Online (Sandbox Code Playgroud)

没有t.Cleanup newTestSubject将不得不返回(*domain.Service, *sql.DB),泄漏有关domain.Service的构造的详细信息。

  • 事实证明,t.Cleanup 对于处理 Helper 函数中的资源确实很有用。谢谢。 (2认同)
  • 很抱歉重提这个老问题。只是想知道,您是否认为调用 `t.Cleanup(d.Close)` 或像您在这里所做的那样使用额外的函数更惯用? (2认同)