Integration testing with
TestСontainers-Go
Nikolay Kuznetsov
GopherCon Europe
18 Jun 2020
@nikolayk812
Slide 2
Slide 2 text
2 unit tests passed, 0 integration tests
Slide 3
Slide 3 text
Basic integration test
Slide 4
Slide 4 text
Getting a database for testing
● Local database
● In-memory mock
● Docker!
Slide 5
Slide 5 text
Docker advantages
● 100% compatible databases
● Same version as production
● Empty DB state
Slide 6
Slide 6 text
Integration testing with Docker
Slide 7
Slide 7 text
Easy-peasy!
docker run -d -p 5432:5432 postgres:12.1
Slide 8
Slide 8 text
● Host port conflicts
● Not ready сontainer / service
● Resource leak (the container keeps running)
● Stale data (if reusing the same container)
What could go wrong?
Slide 9
Slide 9 text
Solving some issues
Slide 10
Slide 10 text
No content
Slide 11
Slide 11 text
Docker API
docs.docker.com/engine/api/latest
Slide 12
Slide 12 text
Exec example
Slide 13
Slide 13 text
No content
Slide 14
Slide 14 text
TestContainers-Go
github.com/testcontainers/testcontainers-go
● Docker Go client under the hood
● Host port randomization
● Containers clean up at the test shutdown
● Readiness waiting strategies
Slide 15
Slide 15 text
As simple as
pgContainer, err := tc.GenericContainer(ctx,
tc.GenericContainerRequest{
ContainerRequest: tc.ContainerRequest{
Image: "postgres:12.1",
ExposedPorts: []string{"5432/tcp"},
},
})
Slide 16
Slide 16 text
Host port randomization
● API to get a host port:
● Prevents port conflicts
● Enables parallel builds
port, err := pgContainer.MappedPort(ctx, "5432/tcp")
Slide 17
Slide 17 text
Containers cleanup: Ryuk
github.com/testcontainers/moby-ryuk
● Ryuk kills containers (networks, volumes) by labels
● TC assigns labels to started containers
● TC keeps a connection to Ryuk open
● Ryuk acts when the connection is terminated
Slide 18
Slide 18 text
Waiting strategies
● Host port
● HTTP
● SQL
● Logs
● Custom
● Multi
Slide 19
Slide 19 text
Host port waiting strategy
● Impl checks both from outside and inside container
● Default (customizable) timeout is 60 seconds
tc.ContainerRequest{
Image: "postgres:12.1",
ExposedPorts: []string{"5432/tcp"},
WaitingFor: wait.ForListeningPort("5432/tcp"),
},