What is useful for t.Cleanup?
Cleanup functions are also called if your test panics, so in your case both would work.
The advantage of using T.Cleanup()
becomes clear if your test calls other functions, passing testing.T
along. Obviously using defer
in those functions would be executed before those functions return, but if you register cleanup functions using T.Cleanup()
, then they will be called only at the end of your test.
Think of T.Cleanup()
as an "improved" and extended version of defer
. It also documents that the passed functions are for cleanup purposes.
t.Cleanup
is useful for cleaning up resources allocated by a helper function when the test does not care about the resource itself.
Example
Consider testing a service layer. The service uses a *sql.DB
but does not create it itself.
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()
})
}
Without t.Cleanup
newTestSubject
would have to return (*domain.Service, *sql.DB)
, leaking details about domain.Service
's construction.
Besides what others have pointed out, t.Cleanup()
is also useful when dealing with parallel subtests, where the cleanup should only run after all subtests have completed. Consider
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()
(...)
})
}
which doesn't work because the test function will return while the the subtests are still running, causing the resources required by the subtests to get wiped out by defer cleanup()
.
Before t.Cleanup()
a way to solve this was to wrap the subtests on another test
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()
(...)
})
})
}
which looks ok, but with t.Cleanup()
it gets way better
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()
(...)
})
}