Slide 1

Slide 1 text

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"), },

Slide 20

Slide 20 text

HTTP waiting strategy WaitingFor: wait.ForHTTP("/health"). WithPort("8080/tcp"). WithStatusCodeMatcher( func(status int) bool { return status == http.StatusOK }),

Slide 21

Slide 21 text

Takeaways ● testcontainers.org ● Balance between flexibility, speed and features ● Great for integration tests ● Possible to use for end-to-end tests

Slide 22

Slide 22 text

Thank you! @nikolayk812 nikolayk812