Level Up Your Integration Testing
With Testcontainers
Dan Vega ⸱ @therealdanvega
Developer Advocate, VMware
⸺
Cora Iberkleid ⸱ @ciberkleid
Developer Advocate, VMware
⸺
Maria Gabriella Brodi ⸱ @BrodiMg
Staff Solutions Engineer, VMware
Abstract - SpringOne Tour
Traditional approaches to integration testing—using shared, local, or in-memory databases—fall short for
today's modern developer.
Developers today are building cloud native distributed microservices and taking advantage of a rich variety
of backing services. This explosion of applications and backing services introduces new challenges in
creating the necessary environments for integration testing. To be useful and effective, these environments
must be easy to create and they must resemble production as closely as possible. New solutions are needed
to make this need a reality.
Enter Testcontainers!
Testcontainers is a Java library that supports JUnit tests and makes it incredibly easy to create lightweight,
throwaway instances of common databases, Selenium web browsers, or anything else that can run in a
Docker container.
Testing is important
Testing can be hard
“You have to make the
right thing to do, the easy
thing to do.”
- Cora Iberkleid
Agenda
● Intro
● Testcontainers 101
● Spring Boot Demo
● Q&A
INTRO
“Prod-like” integration testing - what do we mean?
● Integration vs Unit tests
● Greater fidelity to run-time conditions
○ Type of system
○ Network reliability
○ OS environment
● Shifted left
○ Local developer machine
○ Iterate locally
Common approaches to integration testing
In Integration tests we are interested in verifying the behavior and interactions of
multiple modules.
For this purpose we can use:
● Shared instances
● Local installation
● In memory solutions
In-memory testing
App
Mock server or
in-memory service
Options:
● Mock server (Wiremock, Loki…)
● In-memory service (h2, hsql...)
Limitations of in-memory testing
App
Mock server or
in-memory service
Limitations:
● Requires framework support
● Behavior differences to real system
● Latency/bandwidth testing can be
challenging
● Not every service has an in-memory
option
● Differences in security configuration
New challenges with in-memory testing
App
Challenge exacerbated with explosion in microservices
and variety of service options over the last 10 years.
Mock server or
in-memory service
Limitations:
● Requires framework support
● Behavior differences to real system
● Latency/bandwidth testing can be
challenging
● Not every service has an in-memory
option
● Differences in security configuration
New challenges with in-memory testing
App
Challenge exacerbated with explosion in microservices
and variety of service options over the last 10 years.
Mock server or
in-memory service
Limitations:
● Requires framework support
● Behavior differences to real system
● Latency/bandwidth testing can be
challenging
● Not every service has an in-memory
option
● Differences in security configuration
Containerization Helps!
● Containerization per se is a big part of the solution
● Docker Compose - works for any framework
However…
● Cumbersome lifecycle management
● Additional skill set for developers to learn
However!
● There is an easier solution :)
Testcontainers: easy testing with external services
Service on Docker
App
Testcontainers
dependency
TESTCONTAINERS 101
Testcontainers intro/overview
● Java library that creates instances of Docker containers for testing
● Supports JUnit4, JUnit5, and Spock
Support for other languages
(check GitHub for more complete information)
Available modules
● Provides lightweight,
throwaway instances of
common databases,
Selenium web browsers,
and more
● Can start anything else
that can run in a Docker
container
● Can run containers on
your local Docker or in
the Testcontainers Cloud
SaaS platform (beta)
Available modules
● Provides lightweight,
throwaway instances of
common databases,
Selenium web browsers,
and more
Available modules
● Provides lightweight,
throwaway instances of
common databases,
Selenium web browsers,
and more
Available modules
● Provides lightweight,
throwaway instances of
common databases,
Selenium web browsers,
and more
Available modules
● Provides lightweight,
throwaway instances of
common databases,
Selenium web browsers,
and more
Available modules
● Provides lightweight,
throwaway instances of
common databases,
Selenium web browsers,
and more
Available modules
● Provides lightweight,
throwaway instances of
common databases,
Selenium web browsers,
and more
Available modules
● Provides lightweight,
throwaway instances of
common databases,
Selenium web browsers,
and more
Available modules
● Provides lightweight,
throwaway instances of
common databases,
Selenium web browsers,
and more
● Can start anything else
that can run in a Docker
container
Whale by Artzgeo on Noun Project
And in the
cloud!
Testcontainers
Cloud
SaaS Platform (beta)
https:/
/testcontainers.cloud
Photo by Engin Akyurt on Unsplash
Whale by Artzgeo on Noun Project
X
Why Testcontainers? Where to use them?
● Prod-like systems:
● Easier instantiation of disposable services that lack an in-memory option
● Test against any application that can run as a container
● Integration testing:
● Data access layer integration
● Application integration
● Browser-based acceptance
inspiration...
package org.testcontainers.junit.jupiter;
import ...
class TestcontainersExtension implements BeforeEachCallback, BeforeAllCallback,
AfterEachCallback, AfterAllCallback, ExecutionCondition, TestInstancePostProcessor {
@Testcontainers (JUnit 5)
JUnit5 Extension - intercepts JUnit lifecycle
events and manages container lifecycles
@Testcontainers
public class MyIntegrationTests {
@Container // In JUnit 4, use @Rule/@ClassRule
static PostgreSQLContainer<?> db = new PostgreSQLContainer<>("postgres");
...
@ExtendWith({TestcontainersExtension.class})
@Container (JUnit 5) / @Rule (JUnit 4)
@Testcontainers
public class MyIntegrationTests {
@Container // In JUnit 4, use @Rule/@ClassRule
PostgreSQLContainer<?> db = new PostgreSQLContainer<>("postgres");
...
Flags TestcontainersExtension about a container to manage
● New container is started/stopped for every test
● Declare as static to re-use the same container
JUnit Test Lifecycle & Testcontainer Callbacks
BeforeAllCallback
BeforeEachCallback
AfterAllCallback
AfterEachCallback
ExecutionCondition
TestInstancePostProcessor
JUnit Test Lifecycle
@Testcontainers
@Slf4j
public class LifeCycleTest {
@Container
GenericContainer container = new
GenericContainer(DockerImageName.parse("myRepo/myImg"));
public LifeCycleTest() {
log.info("In Constructor. Class instance: ", this);
}
@BeforeAll
public static void beforeAllMethod() {
log.info("In @BeforeAll. Static method.");
}
@BeforeEach
public void beforeEachMethod() {
log.info("In @BeforeEach. Class instance: " + this
+ ", Container id: ", container.getContainerId());
}
@Test
public void test1() {
log.info("In @Test 1. Class instance: ", this);
}
@Test
public void test2() {
log.info("In @Test 2. Class instance: ", this);
}
@AfterEach
public void afterEachMethod() {
log.info("In @AfterEach. Class instance: ", this);
}
@AfterAll
public static void afterAllMethod() {
log.info("In @AfterAll. Static method.");
}
}
@Testcontainers
@Slf4j
public class LifeCycleTest {
@Container
GenericContainer container = new
GenericContainer(DockerImageName.parse("myRepo/myImg"));
public LifeCycleTest() {
log.info("In Constructor. Class instance: ", this);
}
@BeforeAll
public static void beforeAllMethod() {
log.info("In @BeforeAll. Static method.");
}
@BeforeEach
public void beforeEachMethod() {
log.info("In @BeforeEach. Class instance: " + this
+ ", Container id: ", container.getContainerId());
}
@Test
public void test1() {
log.info("In @Test 1. Class instance: ", this);
}
@Test
public void test2() {
log.info("In @Test 2. Class instance: ", this);
}
@AfterEach
public void afterEachMethod() {
log.info("In @AfterEach. Class instance: ", this);
}
@AfterAll
public static void afterAllMethod() {
log.info("In @AfterAll. Static method.");
}
}
In @BeforeAll. Static method.
No testcontainers
default action in
@BeforeAll
Test 1
@Testcontainers
@Slf4j
public class LifeCycleTest {
@Container
GenericContainer container = new
GenericContainer(DockerImageName.parse("myRepo/myImg"));
public LifeCycleTest() {
log.info("In Constructor. Class instance: ", this);
}
@BeforeAll
public static void beforeAllMethod() {
log.info("In @BeforeAll. Static method.");
}
@BeforeEach
public void beforeEachMethod() {
log.info("In @BeforeEach. Class instance: " + this
+ ", Container id: ", container.getContainerId());
}
@Test
public void test1() {
log.info("In @Test 1. Class instance: ", this);
}
@Test
public void test2() {
log.info("In @Test 2. Class instance: ", this);
}
@AfterEach
public void afterEachMethod() {
log.info("In @AfterEach. Class instance: ", this);
}
@AfterAll
public static void afterAllMethod() {
log.info("In @AfterAll. Static method.");
}
}
In @BeforeAll. Static method.
In Constructor. Class instance: LifeCycleTest@17d88132
No testcontainers action
in Constructor
Test 1
@Testcontainers
@Slf4j
public class LifeCycleTest {
@Container
GenericContainer container = new
GenericContainer(DockerImageName.parse("myRepo/myImg"));
public LifeCycleTest() {
log.info("In Constructor. Class instance: ", this);
}
@BeforeAll
public static void beforeAllMethod() {
log.info("In @BeforeAll. Static method.");
}
@BeforeEach
public void beforeEachMethod() {
log.info("In @BeforeEach. Class instance: " + this
+ ", Container id: ", container.getContainerId());
}
@Test
public void test1() {
log.info("In @Test 1. Class instance: ", this);
}
@Test
public void test2() {
log.info("In @Test 2. Class instance: ", this);
}
@AfterEach
public void afterEachMethod() {
log.info("In @AfterEach. Class instance: ", this);
}
@AfterAll
public static void afterAllMethod() {
log.info("In @AfterAll. Static method.");
}
}
In @BeforeAll. Static method.
In Constructor. Class instance: LifeCycleTest@17d88132
Found Docker environment with local Unix socket (unix:///var/run/docker.sock)
Connected to docker
Pulling docker image: testcontainers/ryuk
Ryuk started - will monitor and terminate Testcontainers containers on JVM exit
Checking the system...
✔ Docker server version should be at least 1.6.0
✔ Docker environment should have more than 2GB free disk space
Pulling docker image: myRepo/myImg
Container myRepo/myImg is starting:
b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5
In @BeforeEach. Class instance: LifeCycleTest@17d88132, Container id:
b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5
@BeforeEach, Test1:
Connect to docker
Start ryuk
Check system
Start container
Execute BeforeEach method
Test 1
@Testcontainers
@Slf4j
public class LifeCycleTest {
@Container
GenericContainer container = new
GenericContainer(DockerImageName.parse("myRepo/myImg"));
public LifeCycleTest() {
log.info("In Constructor. Class instance: ", this);
}
@BeforeAll
public static void beforeAllMethod() {
log.info("In @BeforeAll. Static method.");
}
@BeforeEach
public void beforeEachMethod() {
log.info("In @BeforeEach. Class instance: " + this
+ ", Container id: ", container.getContainerId());
}
@Test
public void test1() {
log.info("In @Test 1. Class instance: ", this);
}
@Test
public void test2() {
log.info("In @Test 2. Class instance: ", this);
}
@AfterEach
public void afterEachMethod() {
log.info("In @AfterEach. Class instance: ", this);
}
@AfterAll
public static void afterAllMethod() {
log.info("In @AfterAll. Static method.");
}
}
In @BeforeAll. Static method.
In Constructor. Class instance: LifeCycleTest@17d88132
Found Docker environment with local Unix socket (unix:///var/run/docker.sock)
Connected to docker
Pulling docker image: testcontainers/ryuk
Ryuk started - will monitor and terminate Testcontainers containers on JVM exit
Checking the system...
✔ Docker server version should be at least 1.6.0
✔ Docker environment should have more than 2GB free disk space
Pulling docker image: myRepo/myImg
Container myRepo/myImg is starting:
b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5
In @BeforeEach. Class instance: LifeCycleTest@17d88132, Container id:
b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5
In @Test 1. Class instance: LifeCycleTest@17d88132
No testcontainers action
in @Test
Test 1
@Testcontainers
@Slf4j
public class LifeCycleTest {
@Container
GenericContainer container = new
GenericContainer(DockerImageName.parse("myRepo/myImg"));
public LifeCycleTest() {
log.info("In Constructor. Class instance: ", this);
}
@BeforeAll
public static void beforeAllMethod() {
log.info("In @BeforeAll. Static method.");
}
@BeforeEach
public void beforeEachMethod() {
log.info("In @BeforeEach. Class instance: " + this
+ ", Container id: ", container.getContainerId());
}
@Test
public void test1() {
log.info("In @Test 1. Class instance: ", this);
}
@Test
public void test2() {
log.info("In @Test 2. Class instance: ", this);
}
@AfterEach
public void afterEachMethod() {
log.info("In @AfterEach. Class instance: ", this);
}
@AfterAll
public static void afterAllMethod() {
log.info("In @AfterAll. Static method.");
}
}
In @BeforeAll. Static method.
In Constructor. Class instance: LifeCycleTest@17d88132
Found Docker environment with local Unix socket (unix:///var/run/docker.sock)
Connected to docker
Pulling docker image: testcontainers/ryuk
Ryuk started - will monitor and terminate Testcontainers containers on JVM exit
Checking the system...
✔ Docker server version should be at least 1.6.0
✔ Docker environment should have more than 2GB free disk space
Pulling docker image: myRepo/myImg
Container myRepo/myImg is starting:
b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5
In @BeforeEach. Class instance: LifeCycleTest@17d88132, Container id:
b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5
In @Test 1. Class instance: LifeCycleTest@17d88132
In @AfterEach. Class instance: LifeCycleTest@17d88132
Ryuk removed container and associated volume(s): myRepo/myImg
After Test 1 “AfterEach”,
Ryuk cleans up container
Test 1
@Testcontainers
@Slf4j
public class LifeCycleTest {
@Container
GenericContainer container = new
GenericContainer(DockerImageName.parse("myRepo/myImg"));
public LifeCycleTest() {
log.info("In Constructor. Class instance: ", this);
}
@BeforeAll
public static void beforeAllMethod() {
log.info("In @BeforeAll. Static method.");
}
@BeforeEach
public void beforeEachMethod() {
log.info("In @BeforeEach. Class instance: " + this
+ ", Container id: ", container.getContainerId());
}
@Test
public void test1() {
log.info("In @Test 1. Class instance: ", this);
}
@Test
public void test2() {
log.info("In @Test 2. Class instance: ", this);
}
@AfterEach
public void afterEachMethod() {
log.info("In @AfterEach. Class instance: ", this);
}
@AfterAll
public static void afterAllMethod() {
log.info("In @AfterAll. Static method.");
}
}
In @BeforeAll. Static method.
In Constructor. Class instance: LifeCycleTest@17d88132
Found Docker environment with local Unix socket (unix:///var/run/docker.sock)
Connected to docker
Pulling docker image: testcontainers/ryuk
Ryuk started - will monitor and terminate Testcontainers containers on JVM exit
Checking the system...
✔ Docker server version should be at least 1.6.0
✔ Docker environment should have more than 2GB free disk space
Pulling docker image: myRepo/myImg
Container myRepo/myImg is starting:
b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5
In @BeforeEach. Class instance: LifeCycleTest@17d88132, Container id:
b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5
In @Test 1. Class instance: LifeCycleTest@17d88132
In @AfterEach. Class instance: LifeCycleTest@17d88132
Ryuk removed container and associated volume(s): myRepo/myImg
In Constructor. Class instance: LifeCycleTest@42d236fb
Constructor creates a
new class instance
Test 2
Test 1
@Testcontainers
@Slf4j
public class LifeCycleTest {
@Container
GenericContainer container = new
GenericContainer(DockerImageName.parse("myRepo/myImg"));
public LifeCycleTest() {
log.info("In Constructor. Class instance: ", this);
}
@BeforeAll
public static void beforeAllMethod() {
log.info("In @BeforeAll. Static method.");
}
@BeforeEach
public void beforeEachMethod() {
log.info("In @BeforeEach. Class instance: " + this
+ ", Container id: ", container.getContainerId());
}
@Test
public void test1() {
log.info("In @Test 1. Class instance: ", this);
}
@Test
public void test2() {
log.info("In @Test 2. Class instance: ", this);
}
@AfterEach
public void afterEachMethod() {
log.info("In @AfterEach. Class instance: ", this);
}
@AfterAll
public static void afterAllMethod() {
log.info("In @AfterAll. Static method.");
}
}
In @BeforeAll. Static method.
In Constructor. Class instance: LifeCycleTest@17d88132
Found Docker environment with local Unix socket (unix:///var/run/docker.sock)
Connected to docker
Pulling docker image: testcontainers/ryuk
Ryuk started - will monitor and terminate Testcontainers containers on JVM exit
Checking the system...
✔ Docker server version should be at least 1.6.0
✔ Docker environment should have more than 2GB free disk space
Pulling docker image: myRepo/myImg
Container myRepo/myImg is starting:
b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5
In @BeforeEach. Class instance: LifeCycleTest@17d88132, Container id:
b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5
In @Test 1. Class instance: LifeCycleTest@17d88132
In @AfterEach. Class instance: LifeCycleTest@17d88132
Ryuk removed container and associated volume(s): myRepo/myImg
In Constructor. Class instance: LifeCycleTest@42d236fb
Container myRepo/myImg is starting:
20e4227f6d5366c7f6ccb2ebf09eb5666bb75d20a4c307c6026870dc0fc0d3a1
In @BeforeEach. Class instance: LifeCycleTest@42d236fb, Container id:
20e4227f6d5366c7f6ccb2ebf09eb5666bb75d20a4c307c6026870dc0fc0d3a1
@BeforeEach, Test2:
Start new container
Execute BeforeEach method
Test 2
Test 1
@Testcontainers
@Slf4j
public class LifeCycleTest {
@Container
GenericContainer container = new
GenericContainer(DockerImageName.parse("myRepo/myImg"));
public LifeCycleTest() {
log.info("In Constructor. Class instance: ", this);
}
@BeforeAll
public static void beforeAllMethod() {
log.info("In @BeforeAll. Static method.");
}
@BeforeEach
public void beforeEachMethod() {
log.info("In @BeforeEach. Class instance: " + this
+ ", Container id: ", container.getContainerId());
}
@Test
public void test1() {
log.info("In @Test 1. Class instance: ", this);
}
@Test
public void test2() {
log.info("In @Test 2. Class instance: ", this);
}
@AfterEach
public void afterEachMethod() {
log.info("In @AfterEach. Class instance: ", this);
}
@AfterAll
public static void afterAllMethod() {
log.info("In @AfterAll. Static method.");
}
}
In @BeforeAll. Static method.
In Constructor. Class instance: LifeCycleTest@17d88132
Found Docker environment with local Unix socket (unix:///var/run/docker.sock)
Connected to docker
Pulling docker image: testcontainers/ryuk
Ryuk started - will monitor and terminate Testcontainers containers on JVM exit
Checking the system...
✔ Docker server version should be at least 1.6.0
✔ Docker environment should have more than 2GB free disk space
Pulling docker image: myRepo/myImg
Container myRepo/myImg is starting:
b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5
In @BeforeEach. Class instance: LifeCycleTest@17d88132, Container id:
b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5
In @Test 1. Class instance: LifeCycleTest@17d88132
In @AfterEach. Class instance: LifeCycleTest@17d88132
Ryuk removed container and associated volume(s): myRepo/myImg
In Constructor. Class instance: LifeCycleTest@42d236fb
Container myRepo/myImg is starting:
20e4227f6d5366c7f6ccb2ebf09eb5666bb75d20a4c307c6026870dc0fc0d3a1
In @BeforeEach. Class instance: LifeCycleTest@42d236fb, Container id:
20e4227f6d5366c7f6ccb2ebf09eb5666bb75d20a4c307c6026870dc0fc0d3a1
In @Test 2. Class instance: LifeCycleTest@42d236fb
No testcontainers action
in @Test
Test 2
Test 1
@Testcontainers
@Slf4j
public class LifeCycleTest {
@Container
GenericContainer container = new
GenericContainer(DockerImageName.parse("myRepo/myImg"));
public LifeCycleTest() {
log.info("In Constructor. Class instance: ", this);
}
@BeforeAll
public static void beforeAllMethod() {
log.info("In @BeforeAll. Static method.");
}
@BeforeEach
public void beforeEachMethod() {
log.info("In @BeforeEach. Class instance: " + this
+ ", Container id: ", container.getContainerId());
}
@Test
public void test1() {
log.info("In @Test 1. Class instance: ", this);
}
@Test
public void test2() {
log.info("In @Test 2. Class instance: ", this);
}
@AfterEach
public void afterEachMethod() {
log.info("In @AfterEach. Class instance: ", this);
}
@AfterAll
public static void afterAllMethod() {
log.info("In @AfterAll. Static method.");
}
}
In @BeforeAll. Static method.
In Constructor. Class instance: LifeCycleTest@17d88132
Found Docker environment with local Unix socket (unix:///var/run/docker.sock)
Connected to docker
Pulling docker image: testcontainers/ryuk
Ryuk started - will monitor and terminate Testcontainers containers on JVM exit
Checking the system...
✔ Docker server version should be at least 1.6.0
✔ Docker environment should have more than 2GB free disk space
Pulling docker image: myRepo/myImg
Container myRepo/myImg is starting:
b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5
In @BeforeEach. Class instance: LifeCycleTest@17d88132, Container id:
b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5
In @Test 1. Class instance: LifeCycleTest@17d88132
In @AfterEach. Class instance: LifeCycleTest@17d88132
Ryuk removed container and associated volume(s): myRepo/myImg
In Constructor. Class instance: LifeCycleTest@42d236fb
Container myRepo/myImg is starting:
20e4227f6d5366c7f6ccb2ebf09eb5666bb75d20a4c307c6026870dc0fc0d3a1
In @BeforeEach. Class instance: LifeCycleTest@42d236fb, Container id:
20e4227f6d5366c7f6ccb2ebf09eb5666bb75d20a4c307c6026870dc0fc0d3a1
In @Test 2. Class instance: LifeCycleTest@42d236fb
In @AfterEach. Class instance: LifeCycleTest@42d236fb
Ryuk removed container and associated volume(s): myRepo/myImg
In @AfterAll. Static method.
After Test 2 “AfterEach”,
Ryuk cleans up container
Test 2
Test 1
@Testcontainers
@Slf4j
public class LifeCycleTest {
@Container
GenericContainer container = new
GenericContainer(DockerImageName.parse("myRepo/myImg"));
public LifeCycleTest() {
log.info("In Constructor. Class instance: ", this);
}
@BeforeAll
public static void beforeAllMethod() {
log.info("In @BeforeAll. Static method.");
}
@BeforeEach
public void beforeEachMethod() {
log.info("In @BeforeEach. Class instance: " + this
+ ", Container id: ", container.getContainerId());
}
@Test
public void test1() {
log.info("In @Test 1. Class instance: ", this);
}
@Test
public void test2() {
log.info("In @Test 2. Class instance: ", this);
}
@AfterEach
public void afterEachMethod() {
log.info("In @AfterEach. Class instance: ", this);
}
@AfterAll
public static void afterAllMethod() {
log.info("In @AfterAll. Static method.");
}
}
In @BeforeAll. Static method.
In Constructor. Class instance: LifeCycleTest@17d88132
Found Docker environment with local Unix socket (unix:///var/run/docker.sock)
Connected to docker
Pulling docker image: testcontainers/ryuk
Ryuk started - will monitor and terminate Testcontainers containers on JVM exit
Checking the system...
✔ Docker server version should be at least 1.6.0
✔ Docker environment should have more than 2GB free disk space
Pulling docker image: myRepo/myImg
Container myRepo/myImg is starting:
b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5
In @BeforeEach. Class instance: LifeCycleTest@17d88132, Container id:
b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5
In @Test 1. Class instance: LifeCycleTest@17d88132
In @AfterEach. Class instance: LifeCycleTest@17d88132
Ryuk removed container and associated volume(s): myRepo/myImg
In Constructor. Class instance: LifeCycleTest@42d236fb
Container myRepo/myImg is starting:
20e4227f6d5366c7f6ccb2ebf09eb5666bb75d20a4c307c6026870dc0fc0d3a1
In @BeforeEach. Class instance: LifeCycleTest@42d236fb, Container id:
20e4227f6d5366c7f6ccb2ebf09eb5666bb75d20a4c307c6026870dc0fc0d3a1
In @Test 2. Class instance: LifeCycleTest@42d236fb
In @AfterEach. Class instance: LifeCycleTest@42d236fb
Ryuk removed container and associated volume(s): myRepo/myImg
In @AfterAll. Static method.
No testcontainers
default action in
@AfterAll
Test 2
Test 1
@Testcontainers
@Slf4j
public class LifeCycleTest {
@Container
static GenericContainer container = new
GenericContainer(DockerImageName.parse("myRepo/myImg"));
public LifeCycleTest() {
log.info("In Constructor. Class instance: ", this);
}
@BeforeAll
public static void beforeAllMethod() {
log.info("In @BeforeAll. Static method.");
}
@BeforeEach
public void beforeEachMethod() {
log.info("In @BeforeEach. Class instance: " + this
+ ", Container id: ", container.getContainerId());
}
@Test
public void test1() {
log.info("In @Test 1. Class instance: ", this);
}
@Test
public void test2() {
log.info("In @Test 2. Class instance: ", this);
}
@AfterEach
public void afterEachMethod() {
log.info("In @AfterEach. Class instance: ", this);
}
@AfterAll
public static void afterAllMethod() {
log.info("In @AfterAll. Static method.");
}
}
@Testcontainers
@Slf4j
public class LifeCycleTest {
@Container
static GenericContainer container = new
GenericContainer(DockerImageName.parse("myRepo/myImg"));
public LifeCycleTest() {
log.info("In Constructor. Class instance: ", this);
}
@BeforeAll
public static void beforeAllMethod() {
log.info("In @BeforeAll. Static method.");
}
@BeforeEach
public void beforeEachMethod() {
log.info("In @BeforeEach. Class instance: " + this
+ ", Container id: ", container.getContainerId());
}
@Test
public void test1() {
log.info("In @Test 1. Class instance: ", this);
}
@Test
public void test2() {
log.info("In @Test 2. Class instance: ", this);
}
@AfterEach
public void afterEachMethod() {
log.info("In @AfterEach. Class instance: ", this);
}
@AfterAll
public static void afterAllMethod() {
log.info("In @AfterAll. Static method.");
}
}
Found Docker environment with local Unix socket (unix:///var/run/docker.sock)
Connected to docker
Ryuk started - will monitor and terminate Testcontainers containers on JVM exit
Checking the system...
✔ Docker server version should be at least 1.6.0
✔ Docker environment should have more than 2GB free disk space
Container myRepo/myImg is starting:
8d8ff20a9f25dd8b0d4cd025406bdebfa9dcf5ab637ef53b5a815d97bc739b5a
In @BeforeAll. Static method.
In Constructor. Class instance: LifeCycleTest@63a5d002
In @BeforeEach. Class instance: LifeCycleTest@63a5d002, Container id:
8d8ff20a9f25dd8b0d4cd025406bdebfa9dcf5ab637ef53b5a815d97bc739b5a
In @Test 1. Class instance: LifeCycleTest@63a5d002
In @AfterEach. Class instance: LifeCycleTest@63a5d002
In Constructor. Class instance: LifeCycleTest@60e949e1
In @BeforeEach. Class instance: LifeCycleTest@60e949e1, Container id:
8d8ff20a9f25dd8b0d4cd025406bdebfa9dcf5ab637ef53b5a815d97bc739b5a
In @Test 2. Class instance: LifeCycleTest@60e949e1
In @AfterEach. Class instance: LifeCycleTest@60e949e1
In @AfterAll. Static method.
Ryuk removed container and associated volume(s): myRepo/myImg
Container is reused
across tests
Test 2
Test 1
Spring Boot ❤ Testcontainers
https://github.com/danvega/reading-list
Q&A

Level Up Your Integration Testing With Testcontainers

  • 1.
    Level Up YourIntegration Testing With Testcontainers Dan Vega ⸱ @therealdanvega Developer Advocate, VMware ⸺ Cora Iberkleid ⸱ @ciberkleid Developer Advocate, VMware ⸺ Maria Gabriella Brodi ⸱ @BrodiMg Staff Solutions Engineer, VMware
  • 2.
    Abstract - SpringOneTour Traditional approaches to integration testing—using shared, local, or in-memory databases—fall short for today's modern developer. Developers today are building cloud native distributed microservices and taking advantage of a rich variety of backing services. This explosion of applications and backing services introduces new challenges in creating the necessary environments for integration testing. To be useful and effective, these environments must be easy to create and they must resemble production as closely as possible. New solutions are needed to make this need a reality. Enter Testcontainers! Testcontainers is a Java library that supports JUnit tests and makes it incredibly easy to create lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container.
  • 3.
  • 4.
  • 5.
    “You have tomake the right thing to do, the easy thing to do.” - Cora Iberkleid
  • 6.
    Agenda ● Intro ● Testcontainers101 ● Spring Boot Demo ● Q&A
  • 7.
  • 8.
    “Prod-like” integration testing- what do we mean? ● Integration vs Unit tests ● Greater fidelity to run-time conditions ○ Type of system ○ Network reliability ○ OS environment ● Shifted left ○ Local developer machine ○ Iterate locally
  • 9.
    Common approaches tointegration testing In Integration tests we are interested in verifying the behavior and interactions of multiple modules. For this purpose we can use: ● Shared instances ● Local installation ● In memory solutions
  • 10.
    In-memory testing App Mock serveror in-memory service Options: ● Mock server (Wiremock, Loki…) ● In-memory service (h2, hsql...)
  • 11.
    Limitations of in-memorytesting App Mock server or in-memory service Limitations: ● Requires framework support ● Behavior differences to real system ● Latency/bandwidth testing can be challenging ● Not every service has an in-memory option ● Differences in security configuration
  • 12.
    New challenges within-memory testing App Challenge exacerbated with explosion in microservices and variety of service options over the last 10 years. Mock server or in-memory service Limitations: ● Requires framework support ● Behavior differences to real system ● Latency/bandwidth testing can be challenging ● Not every service has an in-memory option ● Differences in security configuration
  • 13.
    New challenges within-memory testing App Challenge exacerbated with explosion in microservices and variety of service options over the last 10 years. Mock server or in-memory service Limitations: ● Requires framework support ● Behavior differences to real system ● Latency/bandwidth testing can be challenging ● Not every service has an in-memory option ● Differences in security configuration
  • 14.
    Containerization Helps! ● Containerizationper se is a big part of the solution ● Docker Compose - works for any framework However… ● Cumbersome lifecycle management ● Additional skill set for developers to learn However! ● There is an easier solution :)
  • 15.
    Testcontainers: easy testingwith external services Service on Docker App Testcontainers dependency
  • 16.
  • 17.
    Testcontainers intro/overview ● Javalibrary that creates instances of Docker containers for testing ● Supports JUnit4, JUnit5, and Spock
  • 18.
    Support for otherlanguages (check GitHub for more complete information)
  • 19.
    Available modules ● Provideslightweight, throwaway instances of common databases, Selenium web browsers, and more ● Can start anything else that can run in a Docker container ● Can run containers on your local Docker or in the Testcontainers Cloud SaaS platform (beta)
  • 20.
    Available modules ● Provideslightweight, throwaway instances of common databases, Selenium web browsers, and more
  • 21.
    Available modules ● Provideslightweight, throwaway instances of common databases, Selenium web browsers, and more
  • 22.
    Available modules ● Provideslightweight, throwaway instances of common databases, Selenium web browsers, and more
  • 23.
    Available modules ● Provideslightweight, throwaway instances of common databases, Selenium web browsers, and more
  • 24.
    Available modules ● Provideslightweight, throwaway instances of common databases, Selenium web browsers, and more
  • 25.
    Available modules ● Provideslightweight, throwaway instances of common databases, Selenium web browsers, and more
  • 26.
    Available modules ● Provideslightweight, throwaway instances of common databases, Selenium web browsers, and more
  • 27.
    Available modules ● Provideslightweight, throwaway instances of common databases, Selenium web browsers, and more ● Can start anything else that can run in a Docker container Whale by Artzgeo on Noun Project
  • 28.
    And in the cloud! Testcontainers Cloud SaaSPlatform (beta) https:/ /testcontainers.cloud Photo by Engin Akyurt on Unsplash Whale by Artzgeo on Noun Project X
  • 29.
    Why Testcontainers? Whereto use them? ● Prod-like systems: ● Easier instantiation of disposable services that lack an in-memory option ● Test against any application that can run as a container ● Integration testing: ● Data access layer integration ● Application integration ● Browser-based acceptance
  • 30.
  • 31.
    package org.testcontainers.junit.jupiter; import ... classTestcontainersExtension implements BeforeEachCallback, BeforeAllCallback, AfterEachCallback, AfterAllCallback, ExecutionCondition, TestInstancePostProcessor { @Testcontainers (JUnit 5) JUnit5 Extension - intercepts JUnit lifecycle events and manages container lifecycles @Testcontainers public class MyIntegrationTests { @Container // In JUnit 4, use @Rule/@ClassRule static PostgreSQLContainer<?> db = new PostgreSQLContainer<>("postgres"); ... @ExtendWith({TestcontainersExtension.class})
  • 32.
    @Container (JUnit 5)/ @Rule (JUnit 4) @Testcontainers public class MyIntegrationTests { @Container // In JUnit 4, use @Rule/@ClassRule PostgreSQLContainer<?> db = new PostgreSQLContainer<>("postgres"); ... Flags TestcontainersExtension about a container to manage ● New container is started/stopped for every test ● Declare as static to re-use the same container
  • 33.
    JUnit Test Lifecycle& Testcontainer Callbacks BeforeAllCallback BeforeEachCallback AfterAllCallback AfterEachCallback ExecutionCondition TestInstancePostProcessor
  • 34.
    JUnit Test Lifecycle @Testcontainers @Slf4j publicclass LifeCycleTest { @Container GenericContainer container = new GenericContainer(DockerImageName.parse("myRepo/myImg")); public LifeCycleTest() { log.info("In Constructor. Class instance: ", this); } @BeforeAll public static void beforeAllMethod() { log.info("In @BeforeAll. Static method."); } @BeforeEach public void beforeEachMethod() { log.info("In @BeforeEach. Class instance: " + this + ", Container id: ", container.getContainerId()); } @Test public void test1() { log.info("In @Test 1. Class instance: ", this); } @Test public void test2() { log.info("In @Test 2. Class instance: ", this); } @AfterEach public void afterEachMethod() { log.info("In @AfterEach. Class instance: ", this); } @AfterAll public static void afterAllMethod() { log.info("In @AfterAll. Static method."); } }
  • 35.
    @Testcontainers @Slf4j public class LifeCycleTest{ @Container GenericContainer container = new GenericContainer(DockerImageName.parse("myRepo/myImg")); public LifeCycleTest() { log.info("In Constructor. Class instance: ", this); } @BeforeAll public static void beforeAllMethod() { log.info("In @BeforeAll. Static method."); } @BeforeEach public void beforeEachMethod() { log.info("In @BeforeEach. Class instance: " + this + ", Container id: ", container.getContainerId()); } @Test public void test1() { log.info("In @Test 1. Class instance: ", this); } @Test public void test2() { log.info("In @Test 2. Class instance: ", this); } @AfterEach public void afterEachMethod() { log.info("In @AfterEach. Class instance: ", this); } @AfterAll public static void afterAllMethod() { log.info("In @AfterAll. Static method."); } } In @BeforeAll. Static method. No testcontainers default action in @BeforeAll Test 1
  • 36.
    @Testcontainers @Slf4j public class LifeCycleTest{ @Container GenericContainer container = new GenericContainer(DockerImageName.parse("myRepo/myImg")); public LifeCycleTest() { log.info("In Constructor. Class instance: ", this); } @BeforeAll public static void beforeAllMethod() { log.info("In @BeforeAll. Static method."); } @BeforeEach public void beforeEachMethod() { log.info("In @BeforeEach. Class instance: " + this + ", Container id: ", container.getContainerId()); } @Test public void test1() { log.info("In @Test 1. Class instance: ", this); } @Test public void test2() { log.info("In @Test 2. Class instance: ", this); } @AfterEach public void afterEachMethod() { log.info("In @AfterEach. Class instance: ", this); } @AfterAll public static void afterAllMethod() { log.info("In @AfterAll. Static method."); } } In @BeforeAll. Static method. In Constructor. Class instance: LifeCycleTest@17d88132 No testcontainers action in Constructor Test 1
  • 37.
    @Testcontainers @Slf4j public class LifeCycleTest{ @Container GenericContainer container = new GenericContainer(DockerImageName.parse("myRepo/myImg")); public LifeCycleTest() { log.info("In Constructor. Class instance: ", this); } @BeforeAll public static void beforeAllMethod() { log.info("In @BeforeAll. Static method."); } @BeforeEach public void beforeEachMethod() { log.info("In @BeforeEach. Class instance: " + this + ", Container id: ", container.getContainerId()); } @Test public void test1() { log.info("In @Test 1. Class instance: ", this); } @Test public void test2() { log.info("In @Test 2. Class instance: ", this); } @AfterEach public void afterEachMethod() { log.info("In @AfterEach. Class instance: ", this); } @AfterAll public static void afterAllMethod() { log.info("In @AfterAll. Static method."); } } In @BeforeAll. Static method. In Constructor. Class instance: LifeCycleTest@17d88132 Found Docker environment with local Unix socket (unix:///var/run/docker.sock) Connected to docker Pulling docker image: testcontainers/ryuk Ryuk started - will monitor and terminate Testcontainers containers on JVM exit Checking the system... ✔ Docker server version should be at least 1.6.0 ✔ Docker environment should have more than 2GB free disk space Pulling docker image: myRepo/myImg Container myRepo/myImg is starting: b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5 In @BeforeEach. Class instance: LifeCycleTest@17d88132, Container id: b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5 @BeforeEach, Test1: Connect to docker Start ryuk Check system Start container Execute BeforeEach method Test 1
  • 38.
    @Testcontainers @Slf4j public class LifeCycleTest{ @Container GenericContainer container = new GenericContainer(DockerImageName.parse("myRepo/myImg")); public LifeCycleTest() { log.info("In Constructor. Class instance: ", this); } @BeforeAll public static void beforeAllMethod() { log.info("In @BeforeAll. Static method."); } @BeforeEach public void beforeEachMethod() { log.info("In @BeforeEach. Class instance: " + this + ", Container id: ", container.getContainerId()); } @Test public void test1() { log.info("In @Test 1. Class instance: ", this); } @Test public void test2() { log.info("In @Test 2. Class instance: ", this); } @AfterEach public void afterEachMethod() { log.info("In @AfterEach. Class instance: ", this); } @AfterAll public static void afterAllMethod() { log.info("In @AfterAll. Static method."); } } In @BeforeAll. Static method. In Constructor. Class instance: LifeCycleTest@17d88132 Found Docker environment with local Unix socket (unix:///var/run/docker.sock) Connected to docker Pulling docker image: testcontainers/ryuk Ryuk started - will monitor and terminate Testcontainers containers on JVM exit Checking the system... ✔ Docker server version should be at least 1.6.0 ✔ Docker environment should have more than 2GB free disk space Pulling docker image: myRepo/myImg Container myRepo/myImg is starting: b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5 In @BeforeEach. Class instance: LifeCycleTest@17d88132, Container id: b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5 In @Test 1. Class instance: LifeCycleTest@17d88132 No testcontainers action in @Test Test 1
  • 39.
    @Testcontainers @Slf4j public class LifeCycleTest{ @Container GenericContainer container = new GenericContainer(DockerImageName.parse("myRepo/myImg")); public LifeCycleTest() { log.info("In Constructor. Class instance: ", this); } @BeforeAll public static void beforeAllMethod() { log.info("In @BeforeAll. Static method."); } @BeforeEach public void beforeEachMethod() { log.info("In @BeforeEach. Class instance: " + this + ", Container id: ", container.getContainerId()); } @Test public void test1() { log.info("In @Test 1. Class instance: ", this); } @Test public void test2() { log.info("In @Test 2. Class instance: ", this); } @AfterEach public void afterEachMethod() { log.info("In @AfterEach. Class instance: ", this); } @AfterAll public static void afterAllMethod() { log.info("In @AfterAll. Static method."); } } In @BeforeAll. Static method. In Constructor. Class instance: LifeCycleTest@17d88132 Found Docker environment with local Unix socket (unix:///var/run/docker.sock) Connected to docker Pulling docker image: testcontainers/ryuk Ryuk started - will monitor and terminate Testcontainers containers on JVM exit Checking the system... ✔ Docker server version should be at least 1.6.0 ✔ Docker environment should have more than 2GB free disk space Pulling docker image: myRepo/myImg Container myRepo/myImg is starting: b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5 In @BeforeEach. Class instance: LifeCycleTest@17d88132, Container id: b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5 In @Test 1. Class instance: LifeCycleTest@17d88132 In @AfterEach. Class instance: LifeCycleTest@17d88132 Ryuk removed container and associated volume(s): myRepo/myImg After Test 1 “AfterEach”, Ryuk cleans up container Test 1
  • 40.
    @Testcontainers @Slf4j public class LifeCycleTest{ @Container GenericContainer container = new GenericContainer(DockerImageName.parse("myRepo/myImg")); public LifeCycleTest() { log.info("In Constructor. Class instance: ", this); } @BeforeAll public static void beforeAllMethod() { log.info("In @BeforeAll. Static method."); } @BeforeEach public void beforeEachMethod() { log.info("In @BeforeEach. Class instance: " + this + ", Container id: ", container.getContainerId()); } @Test public void test1() { log.info("In @Test 1. Class instance: ", this); } @Test public void test2() { log.info("In @Test 2. Class instance: ", this); } @AfterEach public void afterEachMethod() { log.info("In @AfterEach. Class instance: ", this); } @AfterAll public static void afterAllMethod() { log.info("In @AfterAll. Static method."); } } In @BeforeAll. Static method. In Constructor. Class instance: LifeCycleTest@17d88132 Found Docker environment with local Unix socket (unix:///var/run/docker.sock) Connected to docker Pulling docker image: testcontainers/ryuk Ryuk started - will monitor and terminate Testcontainers containers on JVM exit Checking the system... ✔ Docker server version should be at least 1.6.0 ✔ Docker environment should have more than 2GB free disk space Pulling docker image: myRepo/myImg Container myRepo/myImg is starting: b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5 In @BeforeEach. Class instance: LifeCycleTest@17d88132, Container id: b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5 In @Test 1. Class instance: LifeCycleTest@17d88132 In @AfterEach. Class instance: LifeCycleTest@17d88132 Ryuk removed container and associated volume(s): myRepo/myImg In Constructor. Class instance: LifeCycleTest@42d236fb Constructor creates a new class instance Test 2 Test 1
  • 41.
    @Testcontainers @Slf4j public class LifeCycleTest{ @Container GenericContainer container = new GenericContainer(DockerImageName.parse("myRepo/myImg")); public LifeCycleTest() { log.info("In Constructor. Class instance: ", this); } @BeforeAll public static void beforeAllMethod() { log.info("In @BeforeAll. Static method."); } @BeforeEach public void beforeEachMethod() { log.info("In @BeforeEach. Class instance: " + this + ", Container id: ", container.getContainerId()); } @Test public void test1() { log.info("In @Test 1. Class instance: ", this); } @Test public void test2() { log.info("In @Test 2. Class instance: ", this); } @AfterEach public void afterEachMethod() { log.info("In @AfterEach. Class instance: ", this); } @AfterAll public static void afterAllMethod() { log.info("In @AfterAll. Static method."); } } In @BeforeAll. Static method. In Constructor. Class instance: LifeCycleTest@17d88132 Found Docker environment with local Unix socket (unix:///var/run/docker.sock) Connected to docker Pulling docker image: testcontainers/ryuk Ryuk started - will monitor and terminate Testcontainers containers on JVM exit Checking the system... ✔ Docker server version should be at least 1.6.0 ✔ Docker environment should have more than 2GB free disk space Pulling docker image: myRepo/myImg Container myRepo/myImg is starting: b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5 In @BeforeEach. Class instance: LifeCycleTest@17d88132, Container id: b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5 In @Test 1. Class instance: LifeCycleTest@17d88132 In @AfterEach. Class instance: LifeCycleTest@17d88132 Ryuk removed container and associated volume(s): myRepo/myImg In Constructor. Class instance: LifeCycleTest@42d236fb Container myRepo/myImg is starting: 20e4227f6d5366c7f6ccb2ebf09eb5666bb75d20a4c307c6026870dc0fc0d3a1 In @BeforeEach. Class instance: LifeCycleTest@42d236fb, Container id: 20e4227f6d5366c7f6ccb2ebf09eb5666bb75d20a4c307c6026870dc0fc0d3a1 @BeforeEach, Test2: Start new container Execute BeforeEach method Test 2 Test 1
  • 42.
    @Testcontainers @Slf4j public class LifeCycleTest{ @Container GenericContainer container = new GenericContainer(DockerImageName.parse("myRepo/myImg")); public LifeCycleTest() { log.info("In Constructor. Class instance: ", this); } @BeforeAll public static void beforeAllMethod() { log.info("In @BeforeAll. Static method."); } @BeforeEach public void beforeEachMethod() { log.info("In @BeforeEach. Class instance: " + this + ", Container id: ", container.getContainerId()); } @Test public void test1() { log.info("In @Test 1. Class instance: ", this); } @Test public void test2() { log.info("In @Test 2. Class instance: ", this); } @AfterEach public void afterEachMethod() { log.info("In @AfterEach. Class instance: ", this); } @AfterAll public static void afterAllMethod() { log.info("In @AfterAll. Static method."); } } In @BeforeAll. Static method. In Constructor. Class instance: LifeCycleTest@17d88132 Found Docker environment with local Unix socket (unix:///var/run/docker.sock) Connected to docker Pulling docker image: testcontainers/ryuk Ryuk started - will monitor and terminate Testcontainers containers on JVM exit Checking the system... ✔ Docker server version should be at least 1.6.0 ✔ Docker environment should have more than 2GB free disk space Pulling docker image: myRepo/myImg Container myRepo/myImg is starting: b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5 In @BeforeEach. Class instance: LifeCycleTest@17d88132, Container id: b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5 In @Test 1. Class instance: LifeCycleTest@17d88132 In @AfterEach. Class instance: LifeCycleTest@17d88132 Ryuk removed container and associated volume(s): myRepo/myImg In Constructor. Class instance: LifeCycleTest@42d236fb Container myRepo/myImg is starting: 20e4227f6d5366c7f6ccb2ebf09eb5666bb75d20a4c307c6026870dc0fc0d3a1 In @BeforeEach. Class instance: LifeCycleTest@42d236fb, Container id: 20e4227f6d5366c7f6ccb2ebf09eb5666bb75d20a4c307c6026870dc0fc0d3a1 In @Test 2. Class instance: LifeCycleTest@42d236fb No testcontainers action in @Test Test 2 Test 1
  • 43.
    @Testcontainers @Slf4j public class LifeCycleTest{ @Container GenericContainer container = new GenericContainer(DockerImageName.parse("myRepo/myImg")); public LifeCycleTest() { log.info("In Constructor. Class instance: ", this); } @BeforeAll public static void beforeAllMethod() { log.info("In @BeforeAll. Static method."); } @BeforeEach public void beforeEachMethod() { log.info("In @BeforeEach. Class instance: " + this + ", Container id: ", container.getContainerId()); } @Test public void test1() { log.info("In @Test 1. Class instance: ", this); } @Test public void test2() { log.info("In @Test 2. Class instance: ", this); } @AfterEach public void afterEachMethod() { log.info("In @AfterEach. Class instance: ", this); } @AfterAll public static void afterAllMethod() { log.info("In @AfterAll. Static method."); } } In @BeforeAll. Static method. In Constructor. Class instance: LifeCycleTest@17d88132 Found Docker environment with local Unix socket (unix:///var/run/docker.sock) Connected to docker Pulling docker image: testcontainers/ryuk Ryuk started - will monitor and terminate Testcontainers containers on JVM exit Checking the system... ✔ Docker server version should be at least 1.6.0 ✔ Docker environment should have more than 2GB free disk space Pulling docker image: myRepo/myImg Container myRepo/myImg is starting: b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5 In @BeforeEach. Class instance: LifeCycleTest@17d88132, Container id: b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5 In @Test 1. Class instance: LifeCycleTest@17d88132 In @AfterEach. Class instance: LifeCycleTest@17d88132 Ryuk removed container and associated volume(s): myRepo/myImg In Constructor. Class instance: LifeCycleTest@42d236fb Container myRepo/myImg is starting: 20e4227f6d5366c7f6ccb2ebf09eb5666bb75d20a4c307c6026870dc0fc0d3a1 In @BeforeEach. Class instance: LifeCycleTest@42d236fb, Container id: 20e4227f6d5366c7f6ccb2ebf09eb5666bb75d20a4c307c6026870dc0fc0d3a1 In @Test 2. Class instance: LifeCycleTest@42d236fb In @AfterEach. Class instance: LifeCycleTest@42d236fb Ryuk removed container and associated volume(s): myRepo/myImg In @AfterAll. Static method. After Test 2 “AfterEach”, Ryuk cleans up container Test 2 Test 1
  • 44.
    @Testcontainers @Slf4j public class LifeCycleTest{ @Container GenericContainer container = new GenericContainer(DockerImageName.parse("myRepo/myImg")); public LifeCycleTest() { log.info("In Constructor. Class instance: ", this); } @BeforeAll public static void beforeAllMethod() { log.info("In @BeforeAll. Static method."); } @BeforeEach public void beforeEachMethod() { log.info("In @BeforeEach. Class instance: " + this + ", Container id: ", container.getContainerId()); } @Test public void test1() { log.info("In @Test 1. Class instance: ", this); } @Test public void test2() { log.info("In @Test 2. Class instance: ", this); } @AfterEach public void afterEachMethod() { log.info("In @AfterEach. Class instance: ", this); } @AfterAll public static void afterAllMethod() { log.info("In @AfterAll. Static method."); } } In @BeforeAll. Static method. In Constructor. Class instance: LifeCycleTest@17d88132 Found Docker environment with local Unix socket (unix:///var/run/docker.sock) Connected to docker Pulling docker image: testcontainers/ryuk Ryuk started - will monitor and terminate Testcontainers containers on JVM exit Checking the system... ✔ Docker server version should be at least 1.6.0 ✔ Docker environment should have more than 2GB free disk space Pulling docker image: myRepo/myImg Container myRepo/myImg is starting: b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5 In @BeforeEach. Class instance: LifeCycleTest@17d88132, Container id: b1c63a2738a6131ac06dd90bc334e621099b8a9d0092cfbc8899954495bd57c5 In @Test 1. Class instance: LifeCycleTest@17d88132 In @AfterEach. Class instance: LifeCycleTest@17d88132 Ryuk removed container and associated volume(s): myRepo/myImg In Constructor. Class instance: LifeCycleTest@42d236fb Container myRepo/myImg is starting: 20e4227f6d5366c7f6ccb2ebf09eb5666bb75d20a4c307c6026870dc0fc0d3a1 In @BeforeEach. Class instance: LifeCycleTest@42d236fb, Container id: 20e4227f6d5366c7f6ccb2ebf09eb5666bb75d20a4c307c6026870dc0fc0d3a1 In @Test 2. Class instance: LifeCycleTest@42d236fb In @AfterEach. Class instance: LifeCycleTest@42d236fb Ryuk removed container and associated volume(s): myRepo/myImg In @AfterAll. Static method. No testcontainers default action in @AfterAll Test 2 Test 1
  • 45.
    @Testcontainers @Slf4j public class LifeCycleTest{ @Container static GenericContainer container = new GenericContainer(DockerImageName.parse("myRepo/myImg")); public LifeCycleTest() { log.info("In Constructor. Class instance: ", this); } @BeforeAll public static void beforeAllMethod() { log.info("In @BeforeAll. Static method."); } @BeforeEach public void beforeEachMethod() { log.info("In @BeforeEach. Class instance: " + this + ", Container id: ", container.getContainerId()); } @Test public void test1() { log.info("In @Test 1. Class instance: ", this); } @Test public void test2() { log.info("In @Test 2. Class instance: ", this); } @AfterEach public void afterEachMethod() { log.info("In @AfterEach. Class instance: ", this); } @AfterAll public static void afterAllMethod() { log.info("In @AfterAll. Static method."); } }
  • 46.
    @Testcontainers @Slf4j public class LifeCycleTest{ @Container static GenericContainer container = new GenericContainer(DockerImageName.parse("myRepo/myImg")); public LifeCycleTest() { log.info("In Constructor. Class instance: ", this); } @BeforeAll public static void beforeAllMethod() { log.info("In @BeforeAll. Static method."); } @BeforeEach public void beforeEachMethod() { log.info("In @BeforeEach. Class instance: " + this + ", Container id: ", container.getContainerId()); } @Test public void test1() { log.info("In @Test 1. Class instance: ", this); } @Test public void test2() { log.info("In @Test 2. Class instance: ", this); } @AfterEach public void afterEachMethod() { log.info("In @AfterEach. Class instance: ", this); } @AfterAll public static void afterAllMethod() { log.info("In @AfterAll. Static method."); } } Found Docker environment with local Unix socket (unix:///var/run/docker.sock) Connected to docker Ryuk started - will monitor and terminate Testcontainers containers on JVM exit Checking the system... ✔ Docker server version should be at least 1.6.0 ✔ Docker environment should have more than 2GB free disk space Container myRepo/myImg is starting: 8d8ff20a9f25dd8b0d4cd025406bdebfa9dcf5ab637ef53b5a815d97bc739b5a In @BeforeAll. Static method. In Constructor. Class instance: LifeCycleTest@63a5d002 In @BeforeEach. Class instance: LifeCycleTest@63a5d002, Container id: 8d8ff20a9f25dd8b0d4cd025406bdebfa9dcf5ab637ef53b5a815d97bc739b5a In @Test 1. Class instance: LifeCycleTest@63a5d002 In @AfterEach. Class instance: LifeCycleTest@63a5d002 In Constructor. Class instance: LifeCycleTest@60e949e1 In @BeforeEach. Class instance: LifeCycleTest@60e949e1, Container id: 8d8ff20a9f25dd8b0d4cd025406bdebfa9dcf5ab637ef53b5a815d97bc739b5a In @Test 2. Class instance: LifeCycleTest@60e949e1 In @AfterEach. Class instance: LifeCycleTest@60e949e1 In @AfterAll. Static method. Ryuk removed container and associated volume(s): myRepo/myImg Container is reused across tests Test 2 Test 1
  • 47.
    Spring Boot ❤Testcontainers https://github.com/danvega/reading-list
  • 48.