Integration testing with
TestСontainers-Go
Erdem Toraman
Nikolay Kuznetsov
GoDays - Berlin
23 Jan 2020
Slide 2
Slide 2 text
About us
● Go developers at Zalando in Helsinki
● Project with ~20 microservices in Go
● User perspective of TestContainers-Go library
Slide 3
Slide 3 text
2 unit tests passed, 0 integration tests
Slide 4
Slide 4 text
Basic integration test
Slide 5
Slide 5 text
Getting a database for testing
● Local database
● In-memory mock
● Docker!
Slide 6
Slide 6 text
Docker advantages
● 100% compatible databases
● Same version as production
● Empty DB state
Slide 7
Slide 7 text
Integration testing with Docker
Slide 8
Slide 8 text
Easy-peasy!
docker run -d -p 5432:5432 postgres:12.1
Slide 9
Slide 9 text
● Host port conflicts
● Not ready сontainer / service
● Resource leak (the container keeps running)
● Stale data (if reusing the same container)
● Starting mechanism both for CI and a local machine
What could go wrong?
Slide 10
Slide 10 text
Solving some issues
Slide 11
Slide 11 text
No content
Slide 12
Slide 12 text
Docker API
docs.docker.com/engine/api/latest
Slide 13
Slide 13 text
Exec example
Slide 14
Slide 14 text
No content
Slide 15
Slide 15 text
TestContainers flavors
Slide 16
Slide 16 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 17
Slide 17 text
As simple as
pgContainer, err := tc.GenericContainer(ctx,
tc.GenericContainerRequest{
ContainerRequest: tc.ContainerRequest{
Image: "postgres:12.1",
ExposedPorts: []string{"5432/tcp"},
},
})
Slide 18
Slide 18 text
Host port randomization
● API to get a host port:
● Prevents port conflicts
● Enables parallel builds
port, err := pgContainer.MappedPort(ctx, "5432/tcp")
Slide 19
Slide 19 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
Slide 20
Slide 20 text
Waiting strategies
● Host port
● HTTP
● Logs
● Custom
● Multi
Slide 21
Slide 21 text
Host port waiting strategy
● Default (customizable) timeout is 60 seconds
● Impl checks both from outside and inside container
tc.ContainerRequest{
Image: "postgres:12.1",
ExposedPorts: []string{"5432/tcp"},
WaitingFor: wait.ForListeningPort("5432/tcp"),
},
Demo I: integration testing
User Repository
github.com/erdemtoraman/godays-testcontainers-demo
● Testing interaction with a database
● Create and Get users from Postgres
Slide 25
Slide 25 text
What happened?
● Ran a Postgres container
● Mapped a random port to the host machine
● Ran tests against it
● Cleaned up the containers
Slide 26
Slide 26 text
Demo II: end-to-end testing
User Service
github.com/erdemtoraman/godays-testcontainers-demo
Ticket Service
Docker Network
Tests
HTTP
Slide 27
Slide 27 text
Takeaways
● testcontainers.org
● Balance between flexibility, speed and features
● Great for integration tests
● Possible to use for end-to-end tests