Slide 1

Slide 1 text

Integration testing with TestСontainers and JUnit 5 Nikolay Kuznetsov @nikolayk812 Helsinki JUG Zalando 9 December 2019

Slide 2

Slide 2 text

About me ● Go developer at Zalando Wardrobe ● 6+ years of Java experience ● Conference speaker: ○ Voxxed Days Cluj, Container Days Hamburg ● TestContainers-Go contributor

Slide 3

Slide 3 text

Why integration testing?

Slide 4

Slide 4 text

2 unit tests, 0 integration tests

Slide 5

Slide 5 text

Basic integration test

Slide 6

Slide 6 text

Trade-offs Test Runtime

Slide 7

Slide 7 text

Integration testing evolution ● In-memory mocking ● Local DBs ● Vagrant ● Docker / Docker Compose ● Docker API

Slide 8

Slide 8 text

Docker advantages ● 100% compatible database ● Same version as production ● Empty or known state

Slide 9

Slide 9 text

Docker architecture

Slide 10

Slide 10 text

How to start a container for test? ● Shell scripts ● Maven plugin ● Docker Compose ● Docker API ● MiniKube, Kubernetes

Slide 11

Slide 11 text

Shell scripts

Slide 12

Slide 12 text

Maven plugins github.com/fabric8io/docker-maven-plugin

Slide 13

Slide 13 text

Docker Compose

Slide 14

Slide 14 text

Docker API docs.docker.com/engine/api/latest

Slide 15

Slide 15 text

Exec example

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

TestContainers flavors

Slide 18

Slide 18 text

TestContainers Java ● github.com/testcontainers/testcontainers-java ● Wraps docker-java library ● Docker environment discovery ● Host port randomization ● Containers clean up on JVM shutdown ● Readiness waiting strategies

Slide 19

Slide 19 text

As simple as var redis = new GenericContainer("redis:5.0.6") .withExposedPorts(6379); var postgres = new PostgreSQLContainer();

Slide 20

Slide 20 text

Docker environment discovery

Slide 21

Slide 21 text

Talking to Docker via UDS ● curl --unix-socket /var/run/docker.sock http:/localhost/containers/json ● curl --unix-socket /var/run/docker.sock http:/localhost/networks

Slide 22

Slide 22 text

Host port randomization ● To prevent port conflicts ● Enables parallel builds ● API to get a host port

Slide 23

Slide 23 text

Containers cleanup https://github.com/testcontainers/moby-ryuk

Slide 24

Slide 24 text

Waiting strategies ● Host port ● HTTP ● Log message ● Docker healthcheck ● Combined / Custom

Slide 25

Slide 25 text

Host port waiting strategy ● Default: at first exposed port with timeout of 60s ● Both from outside and inside container

Slide 26

Slide 26 text

Internal port check

Slide 27

Slide 27 text

HTTP waiting strategy ● Status & response body predicate

Slide 28

Slide 28 text

Demo setup User Service

Slide 29

Slide 29 text

Demo scenario User Service INSERT INTO SELECT FROM

Slide 30

Slide 30 text

Demo github.com/nikolayk812/hjug-tc-demo

Slide 31

Slide 31 text

Demo recap ● JUnit 5 Extension API ● TestContainer modules

Slide 32

Slide 32 text

JUnit 5 extension points ● Life-cycle callbacks ● Conditional execution ● Parameter resolution ● Exception handling

Slide 33

Slide 33 text

JUnit 5 extension logic ● Implement interface(s) from o.j.j.api.extension package ○ i.e. BeforeEachCallback, ExecutionCondition ● Register with @ExtendsWith annotation ● See @Testcontainers for reference

Slide 34

Slide 34 text

TestContainers modules ● Preconfigured, optimized for testing ● Wrappers on top of GenericContainer class ● 14 databases ● MockServer, LocalStack, Kafka, ToxiProxy

Slide 35

Slide 35 text

Demo-2: setup User Service

Slide 36

Slide 36 text

Demo-2: Docker network User Service user-alias: 8083 postgres-alias: 5432 localhost: 32812

Slide 37

Slide 37 text

Demo-2: scenario User Service POST /users GET /user/

Slide 38

Slide 38 text

Demo-2 github.com/nikolayk812/hjug-tc-demo

Slide 39

Slide 39 text

Demo-2: recap ● Docker network and alias

Slide 40

Slide 40 text

Why end-to-end testing? ● Business flows across multiple services ● Regression, when ○ + new service ○ - legacy service

Slide 41

Slide 41 text

Some cluster Spring Cloud Kubernetes

Slide 42

Slide 42 text

E2E strategies ● Against a deployed cluster ● Against on-demand in memory cluster

Slide 43

Slide 43 text

Deployed cluster cons ● Replace a service with a newer version => instability ● Temporary service name => non-discoverable ● Unexpected databases states ○ Care to clear data after the test?

Slide 44

Slide 44 text

On-demand cluster cons ● Time to start all containers ● Memory + CPU ● How actually to create it?

Slide 45

Slide 45 text

On-demand Kubernetes for E2E?

Slide 46

Slide 46 text

YAGNI

Slide 47

Slide 47 text

On-demand cluster TC approach ● Each service started by TestContainers ● Shared Docker network ● Functional tests ● Unless testing Kubernetes manifests

Slide 48

Slide 48 text

E2E setup User Service Item Service

Slide 49

Slide 49 text

Hints ● Host port forwarding Testcontainers.exposeHostPorts() ● Fixed host port (for remote debugging) GenericContainer.addFixedExposedPort() ● Reusable containers github.com/testcontainers/testcontainers-java/issues/781

Slide 50

Slide 50 text

Takeaways ● https://testcontainers.org ● Balance between flexibility, speed and features ● Works on Mac, Linux, Windows ● Great for integration tests! ● Possible to use for end-to-end tests

Slide 51

Slide 51 text

Thank you! @nikolayk812 nikolayk812 nikolayk812