Often we need per-test setup and teardown. This is especially useful for third party integration testing, for e.g. testing against a database.
It looks fairly simple. Extending the example from a previous post: Nifty Go Pkg: ory/dockertest
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
func redisRig(t *testing.T, fn func(rdb *redis.Client)) {
var err error
pool, err := dockertest.NewPool("")
assert.Nil(t, err)
resource, err := pool.Run("redis", "5.0.6", nil)
assert.Nil(t, err)
var rdb *redis.Client
err = pool.Retry(func() error {
rdb = newRedisClient(resource.GetPort("6379/tcp"))
return rdb.Ping().Err()
})
assert.Nil(t, err)
defer rdb.Close()
// run even if the test fails
defer func() {
// remove resources
err = pool.Purge(resource)
assert.Nil(t, err)
}()
// call test function
fn(rdb)
}
|
Use it in a test.
1
2
3
4
5
6
7
8
9
|
func TestService(t *testing.T) {
redisRig(t, func(rdb *redis.Client) {
svc, err := newService(rdb)
if err != nil {
t.Errorf("new service err %v",err)
return
}
})
}
|
The catch here is to use T.Errorf or similar, so that we don’t fatally exit the test using t.Fatal
. If we exit fatally, cleanup code won’t run:
1
2
3
4
5
6
|
// run even if the test fails
defer func() {
// remove resources
err = pool.Purge(resource)
assert.Nil(t, err)
}()
|
T.Errorf
internally calls T.Fail
which fails the test but execution continues.
This pattern can be used for: setup/teardown databases, constructing clients to third party services etc.