Чем полезен t.Cleanup?

Вопросы

Я хотел бы знать пример использования t.Cleanupвведено в Go1.14. В чем удобство t.Cleanup по сравнению с использованием defer?

https://golang.org/pkg/testing/.

  • Образец

Например, предположим, что мы создаем временный каталог, и при его тестировании мы хотим удалить созданный нами временный каталог.

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...
}

Например, в приведенной ниже замечательной статье описываются проблемы, возникающие при возникновении паники, но deferвызывается даже тогда, когда возникает паника, я думаю. Так что я так и не понял, что может пригодиться.

https://www.gopherguides.com/articles/test-cleanup-in-go-1-14/

Что полезно для t.Cleanup?

2 ответа

Решение

Функции очистки также вызываются, если ваш тест вызывает панику, поэтому в вашем случае оба будут работать.

Преимущество использования T.Cleanup() становится понятным, если ваш тест вызывает другие функции, передавая testing.Tвместе. Очевидно, используяdefer в этих функциях будут выполняться до возврата этих функций, но если вы зарегистрируете функции очистки, используя T.Cleanup(), то они будут вызываться только в конце вашего теста.

Думать о T.Cleanup() как "улучшенная" и расширенная версия defer. Он также документирует, что переданные функции предназначены для очистки.

t.Cleanup полезен для очистки ресурсов, выделенных вспомогательной функцией, когда тест не заботится о самом ресурсе.

пример

Рассмотрите возможность тестирования уровня обслуживания. Сервис использует*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()
  })
}

Без t.Cleanup newTestSubject пришлось бы вернуться (*domain.Service, *sql.DB), утечка информации о domain.Serviceстроительство.

Помимо того, что указывали другие, 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()
      (...)
   })
}

который не работает, потому что функция тестирования вернется, пока подтесты все еще работают, в результате чего ресурсы, необходимые для подтестов, будут уничтожены 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()
         (...)
      })
   })
}

что выглядит нормально, но с 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()
      (...)
   })
}
Другие вопросы по тегам