Testcontainers
The Past, the Present and the Future
- Blockchain Research Group Lead @ Institute for Internet Security
- PhD Student @ RWTH Aachen
- Consultant & Coach
- Testcontainers Maintainer and Open Source Enthusiast
-
Blockchain Activities
- Bloxberg Consortium
- Sovrin Steward Council
- Oracle Groundbreaker Ambassador
Kevin Wittek @kiview
Testing Honeycomb
GenericContainer redis =
new GenericContainer("redis:3.0.2")
.withExposedPorts(6379);
redis.start();
// test my stuff
redis.stop();
The Project
- testcontainers-java first released in 2015
- 100% OSS, MIT licensed
- 83 releases, 273 contributors
- Core maintainers
- Richard North
- Sergei Egorov
- Kevin Wittek
- Forks in Python, C#, Rust, Go, JS; Scala wrapper
@whichrich
@bsideup
Capabilities
- Generic docker container support
- use any Docker image to support tests
- Databases (many!) and Stream processing (Kafka, Pulsar)
- AWS mocks (Localstack)
- Docker Compose
- Selenium
- Chaos testing
Capabilities (2)
- Dynamic port binding and API
- WaitStrategies
- Docker environment discovery (e.g. docker-machine, DOCKER_HOST, Docker for Mac, Docker for Windows)
- Platform independent
- Linux, macOS, Windows 10 (with NPIPE support!)
Testcontainers-Jupiter (JUnit5)
@Testcontainers
class SomeTest {
@Container
private MySQLContainer mySQLContainer = new MySQLContainer();
@Test
void someTestMethod() {
String url = mySQLContainer.getJdbcUrl();
// create a connection and run test as normal
}
}
Demo?
DOs
- Use Testcontainers
- Use copyFileToContainer
DONT's
- Use fixed host ports
- Disable Ryuk without understanding the implications
- Assume Podman is a Docker drop-in replacement
What happened so far...
Sep, 2018 (1.9.x)
- OkHttp by default
- Windows npipe support
- Registry auth support on Windows
- Fix local compose on Windows
- Host ports exposing
Testcontainers.exposeHostPorts(localServerPort);
final String rootUrl =
String.format("http://host.testcontainers.internal:%d/", localServerPort);
Sep, 2018 (1.9.x)
- Dynamic port binding in Couchbase module
- New modules
- ClickHouse
- PostGIS
Nov, 2018 (1.10.x)
- JUnit5 (Jupiter) support
- Documentation relaunch
- Code examples from as part of CI
- ENV var to turn off Ryuk (TESTCONTAINERS_RYUK_DISABLED=true)
- shm + tmpfs settings
- Dependabot
- New modules
- Neo4j & Elasticsearch
You can see an example test that could
have been written for it (without using Testcontainers):
<!--codeinclude-->
[Pre-Testcontainers test code]
(../examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java)
block:RedisBackedCacheIntTestStep0
<!--/codeinclude-->
Dependabot PRs
Dependabot FTW!
Mar, 2019 (1.11.x)
- Chaos testing support (toxiproxy)
- fsync=off for PostgreSQL module
- Drop Netty transport
- Reworking shading
Jul, 2019 (1.12.x)
- dependsOn API
- Improved pull handling
- CI for Windows!
- Azure Pipelines with private Windows node
- New modules
- DB2, CockroachDB & RabbitMQ
GenericContainer container = new GenericContainer()
.dependsOn(postgresContainer, kafkaContainer);
Jul, 2019 (1.12.x)
- Secure logging defaults for properties and environment variables
- Use official Oracle JDBC driver
- Support for DockerClient event streaming
- Docker Compose --build support
- Image pull policies (AgeBasedPullPolicy)
new GenericContainer<>(imageName)
.withImagePullPolicy(PullPolicy.alwaysPull())
AgeBasedPullPolicy oneHour = new AgeBasedPullPolicy(
Duration.of(1L, ChronoUnit.HOURS)
);
new GenericContainer<>(imageName)
.withImagePullPolicy(oneHour)
Mar, 2020 (1.13.x)
- Access Docker Compose containers by service name
- New modules
- Presto & OrientDB
public DockerComposeContainer environment =
new DockerComposeContainer(new File("src/test/resources/compose-test.yml"))
.withExposedService("redis_1", REDIS_PORT)
.withExposedService("db_1", 3306);
Optional<ContainerState> result = environment.getContainerByServiceName("db_1");
Apr, 2020 (1.14.x)
- docker-java updated to 3.2.x (deprecations!)
- R2DBC support
- Improved Couchbase support
- JUnit Jupiter and Spock is now TestLifecycleAware
- Supporting VNC recording
r2dbc:tc:mysql:///databasename?TC_IMAGE_TAG=5.7.22
Apr, 2020 (1.14.x)
- New modules!
- MongoDB
- Apache Solr
- Migrated away from using quay.io for default images
Oct, 2020 (1.15.x )
- Support for rootless Docker!
- Deprecation of ambiguous constructors
- Un-shade docker-java-api
- Optional Apache HttpClinet 5 transport
- GCloud module
- Confluent 6 and MongoDB 4.4 support
- Docker for Mac fixes
// deprecrate
new XyzContainer()
new XyzContainer(String)
// in favor for
new XyzContainer(DockerImageName)
// allowing
new XyzContainer( DockerImageName.parse( "the/image:tag" ) )
Oct, 2020 (1.15.x)
- Image compatibility check, less magic!
- (Scrollable) MP4 videos from VncContainer
- Docker 20.10 compatibility
- Issue: "No such image: testcontainers/ryuk:0.3.0"
- Image name substitution (Docker Hub rate limiting)
- Many bug fixes...
// will just work
new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:any"));
// will initially fail
new KafkaContainer(DockerImageName.parse("some-other-kafka"));
// be explicit instead!
new KafkaContainer(DockerImageName.parse("some-other-kafka"))
.asCompatibleSubstituteFor("confluentinc/cp-kafka");
Future
and Common Pitfalls...
SELF-typing is sometimes a PITA
- Pattern defined in "Effective Java"
- Generic type with a recursive type paramter
- MyClass<T extends MyClass<T>>
- Doesn't play nice with other JVM languages (Scala, Kotlin)
- Confusing for new users and contributors
- Not consistent with all modules
Ideas
- Using functional Builder-Pattern
- Maybe Lombok @SuperBuilder?
- Double Brace Initialization
- People don't like this 😱
Double Brace Initialization 😜
private GenericContainer myContainer = new GenericContainer("myImage:42.23") {{
withExposedPorts(4711);
if (System.getenv().get("INSIDE_CI") == null) {
org.testcontainers.Testcontainers.exposeHostPorts(8080);
}
}};
Existing Kotlin 😱
val redisContainer = GenericContainer<Nothing>("redis:3-alpine")
.apply {
withExposedPorts(6379)
}
TDD workflows
- Container startup introduces overhead
- Testcontainers should be ephemeral
- We still want to avoid test pollution and integrated tests
Released in 1.12.3
Moar Future
- Testcontainers 2.0
- Complete Podman support
- Working with RedHat on making the TC test suite feature parity reference test suite :)
- Container-Core
- Test-Framework agnostic
- OO abstraction of Docker containers
- Other languages
- More languages and aligning existing forks
- GraalVM (mind=blown)
Fire in OSS land 🔥
Fire in OSS land 🔥🔥
Peace restored 🤗
Far Future...
TODO: add Safe Harbor Statement here
- Windows Containers on Windows (WCOW)
- Orchestrators (Docker Swarm, Kubernetes)
- Maybe one day...
- Other container engines (e.g. Podman, Firecracker)
- Build and test inside containers with full IDE support
- Cloud IDEs
- Visual Studio Code Remote Development
- The goal: Improve dev productivity!
GitHub Sponsors
Testcontainers - The Past, the Present and the Future
By Kevin Wittek
Testcontainers - The Past, the Present and the Future
- 1,286