30
Integration testing with Docker Compose and JUnit Dariusz Lorenc Boris Kravtsov

Automated integration testing of distributed systems with Docker Compose and JUnit

Embed Size (px)

Citation preview

Page 1: Automated integration testing of distributed systems with Docker Compose and JUnit

Integration testing with Docker Compose and JUnit

Dariusz Lorenc Boris Kravtsov

Page 2: Automated integration testing of distributed systems with Docker Compose and JUnit

Pivotal Labs

Page 3: Automated integration testing of distributed systems with Docker Compose and JUnit

Problem

End-2-end test of GemFire in various cluster configurations and with different failover scenarios

Page 4: Automated integration testing of distributed systems with Docker Compose and JUnit

GemFire

GemFire is a distributed, in-memory database with strong data consistency, built to support transactional applications with low latency

and high concurrency needs

Page 5: Automated integration testing of distributed systems with Docker Compose and JUnit

GemFire Server

GemFire

GemFire Server GemFire Server

Partitioned Data

Partitioned Data

Partitioned Data

GemFire Server GemFire Server GemFire Server

Partitioned Data

Partitioned Data

Partitioned Data

Client

WAN / Multi-Site

Gateway Hub

Gateway Hub

Relational Database

Page 6: Automated integration testing of distributed systems with Docker Compose and JUnit

Docker Compose to the rescue

Docker Compose is a tool for defining and running multi-container Docker applications

Page 7: Automated integration testing of distributed systems with Docker Compose and JUnit

Docker Compose JUnit Rule

A JUnit rule to manage docker containers using docker-compose

• Start and stop docker-compose multi-container applications

• Waits for services to become available before running tests

• Extends logging and debugging

https://github.com/palantir/docker-compose-rule

Page 8: Automated integration testing of distributed systems with Docker Compose and JUnit

GreetingCounter

Master

Our Sample Distributed System

Page 9: Automated integration testing of distributed systems with Docker Compose and JUnit

Test First - Happy Path

@Test public void shouldReturnData(){ get("/info") .then().assertThat() .body("counter", isA(Number.class)) .body("greeting", is("Hello World")); }

Page 10: Automated integration testing of distributed systems with Docker Compose and JUnit

Spring Boot - Application.kt

@SpringBootApplication open class Application { companion object { @JvmStatic fun main(args: Array<String>) { SpringApplication.run(Application::class.java, *args) } } }

Page 11: Automated integration testing of distributed systems with Docker Compose and JUnit

Greeting Service

@RestController class Controller { @RequestMapping("/greeting") fun greet(): Greeting { return Greeting("Hello World") } }

data class Greeting(val greeting: String)

Page 12: Automated integration testing of distributed systems with Docker Compose and JUnit

Counter Service

@RestController class Controller { val counter = AtomicLong() @RequestMapping("/counter") fun count(): Counter { return Counter(counter.incrementAndGet()) } }

data class Counter(val counter: Long)

Page 13: Automated integration testing of distributed systems with Docker Compose and JUnit

Master Service

@RestController class Controller @Autowired constructor( val greetingClient: GreetingClient, val counterClient: CounterClient) { @RequestMapping("/info") fun info(): Response { return Response(counterClient.counter().counter, greetingClient.greeting().greeting) } }

data class Response(val counter: Long, val greeting: String)

Page 14: Automated integration testing of distributed systems with Docker Compose and JUnit

Master Service - Greeting Client

@FeignClient(name = "greeting", url = "http://greeting-service:8080") interface GreetingClient { @RequestMapping(value = "/greeting", method = arrayOf(RequestMethod.GET)) fun greeting(): Greeting }

Page 15: Automated integration testing of distributed systems with Docker Compose and JUnit

Master Service - Counter Client

@FeignClient(name = "counter", url = "http://counter-service:8080") interface CounterClient { @RequestMapping(value = “/counter”, method = arrayOf(RequestMethod.GET)) fun counter(): Counter }

Page 16: Automated integration testing of distributed systems with Docker Compose and JUnit

GreetingCounter

Master

Next Step: Dockerize Our Services

Page 17: Automated integration testing of distributed systems with Docker Compose and JUnit

Dockerfiles

FROM frolvlad/alpine-oraclejdk8:slim

ADD master-service-1.0.0.jar app.jar

EXPOSE 8080

ENTRYPOINT ["java","-jar","/app.jar"]

Base image

Add the app’s fat jar

Expose the port

Start the java app

Page 18: Automated integration testing of distributed systems with Docker Compose and JUnit

Build Docker Image with Gradle (Transmodo plugin)

buildscript { dependencies { classpath('se.transmode.gradle:gradle-docker:1.2') } } group = '<your docker group name>' apply plugin: 'docker' task buildDocker(type: Docker, dependsOn: build) { push = true applicationName = jar.baseName dockerfile = file('src/main/docker/Dockerfile') doFirst { copy { from jar into stageDir } } }

Gradle Docker plugin

Your Docker Hub ID

Build Docker task

Page 19: Automated integration testing of distributed systems with Docker Compose and JUnit

GreetingCounter

MasterGreetingCount

“Docker Composed” Services

Docker Compose

Page 20: Automated integration testing of distributed systems with Docker Compose and JUnit

docker-compose.ymlGreeting

Counter

Master

greeting-service: image: lorenc/greeting-service ports: - “8081:8080" counter-service: image: lorenc/counter-service ports: - "8082:8080" master-service: image: lorenc/master-service ports: - "8083:8080" links: - greeting-service - counter-service

Page 21: Automated integration testing of distributed systems with Docker Compose and JUnit

Integration Test Setup

@Rule public DockerComposeRule docker = DockerComposeRule.builder() .file("../docker-compose.yml") .build();

@Before public void setUp() throws Exception { docker.dockerCompose().up(); } @After public void tearDown() throws Exception { docker.dockerCompose().down(); }

Page 22: Automated integration testing of distributed systems with Docker Compose and JUnit

Health Checks & Logs

@Rule public DockerComposeRule docker = DockerComposeRule.builder() .file("../docker-compose.yml") .waitingForService("greeting-service", toRespondOverHttp(8080, TO_EXTERNAL_URI)) .waitingForService("counter-service", toRespondOverHttp(8080, TO_EXTERNAL_URI)) .waitingForService("master-service", toRespondOverHttp(8080, TO_EXTERNAL_URI)) .saveLogsTo("build/docker-logs") .build();

Page 23: Automated integration testing of distributed systems with Docker Compose and JUnit
Page 24: Automated integration testing of distributed systems with Docker Compose and JUnit

What could possibly go wrong?

@Test public void shouldReturnHolaWhenGreetingServiceDown(){ docker.dockerCompose().container("greeting-service").stop(); get("/info") .then().assertThat() .body("greeting", is("Hola!")); }

Page 25: Automated integration testing of distributed systems with Docker Compose and JUnit

What could possibly go wrong?

@Test public void shouldReturn42WhenCounterServiceDown() { docker.dockerCompose().container("counter-service").stop(); get("/info") .then().assertThat() .body("counter", is(42)); }

Page 26: Automated integration testing of distributed systems with Docker Compose and JUnit

@FeignClient(name = "greeting", url = "http://greeting-service:8080", fallback = DefaultGreeting::class) interface GreetingClient { @RequestMapping(value = "/greeting", method = arrayOf(RequestMethod.GET)) fun greeting(): Greeting }

@Component class DefaultGreeting : GreetingClient { override fun greeting(): Greeting { return Greeting("Hola!") } }

Master Service - Greeting Client

Page 27: Automated integration testing of distributed systems with Docker Compose and JUnit

Master Service - Counter Client@FeignClient(name = "counter", url = "http://counter-service:8080", fallback = DefaultCounter::class) interface CounterClient { @RequestMapping(value = “/counter”, method = arrayOf(RequestMethod.GET)) fun counter(): Counter }

@Component class DefaultCounter : CounterClient { override fun counter(): Counter { return Counter(42) } }

Page 28: Automated integration testing of distributed systems with Docker Compose and JUnit

Demo

Page 29: Automated integration testing of distributed systems with Docker Compose and JUnit

References https://github.com/d-lorenc/junit-docker-demo https://github.com/palantir/docker-compose-rule

Page 30: Automated integration testing of distributed systems with Docker Compose and JUnit