Чем полезен 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()
(...)
})
}