What Are Testcontainers and Why Should You Care?
Testcontainers reduce the friction of setting up and tearing down test environments, simplifying testing for more confident, robust development.
Translated from What Is Testcontainers, and Why Should You Care?, author Kevin Wittek.
In modern software development, with the continued trend towards distributed systems and microservices architectures, as well as huge integration interfaces, writing software also means integrating with other systems. Integration tests are an excellent tool to ensure the continued correctness of the system under test, and can provide fast and continuous feedback on the system's behavior during the development cycle.
However, integration tests often require external dependencies, such as databases, message brokers, or web servers, all of which have their own configuration and peculiarities to run correctly. Traditionally, managing these dependencies has been cumbersome, prone to inconsistencies, and difficult to replicate on different machines.
Historically, this has made integration tests notorious for being expensive to write and maintain. You either have to set up the environment in a laborious manual manner following possibly outdated documentation (and end up with a slightly broken environment), or use a centrally maintained shared test environment, which often results in test pollution.
This is where Testcontainers comes in. My colleague Oleg Šelajev will present this at All Things Open 2024 in his talk titled “Making your own Testcontainers module is fun and profitable!”
Understanding Testcontainers
Testcontainers is an open source library for provisioning disposable, lightweight instances of databases, message brokers, web browsers, or nearly anything that can run in a Docker container. Testcontainers solves the problem of environment management during testing and development by leveraging Docker to launch lightweight, isolated instances of these services from your codebase as needed.
Testcontainers allows developers to create reliable and repeatable test and development environments with minimal effort using an Infrastructure as Code (IaC) approach. It uses familiar languages to write production and test code and helps ensure that code is tested against real, consistent services. This approach reduces the friction of setting up and tearing down test environments and makes tests more reliable and easier to maintain. For developers, Testcontainers is a game-changing product that simplifies the testing process and enables more confident and robust development.
Cleaning
Testcontainers also automatically cleans up any Docker resources it creates, ensuring your system is left clean after running your tests. This cleanup process integrates seamlessly with the testing framework you are using (such as JUnit), where the container is automatically stopped and deleted after test execution.
Additionally, Testcontainers relies on a dedicated helper container called Ryuk, which monitors and ensures that all resources are properly cleaned up, even in situations where the test process might crash or terminate unexpectedly. By tying this cleanup process to the lifecycle of the test process, and using Ryuk as a watchdog, Testcontainers guarantees that no stray containers, networks, or volumes are left behind, keeping your environment clean and minimizing the risk of resource exhaustion or conflicts in subsequent test runs.
Modules
Testcontainers provides a rich set of modules that encapsulate best practices for using containers in a testing context, making it easier to integrate various technologies into your test suite. These modules are pre-configured Docker containers tailored for specific technologies such as databases (e.g. PostgreSQL, MySQL), message brokers (e.g. Kafka, RabbitMQ) or even full-fledged application environments for browser testing like Selenium.
By using these modules, developers can take advantage of tried and tested configurations that are optimized for reliability and efficiency in testing scenarios. The Testcontainers module catalog provides a comprehensive list of available modules, allowing you to quickly find and implement the containerized services you need.
The following two minimal examples show how to define a Docker container using the Redis image, configure its exposed ports, and start the container in a way that waits for the Redis application inside the container to be ready, in Java and Go.
In Java:
GenericContainer redis = new GenericContainer("redis:5.0.3-alpine")
.withExposedPorts(379);
redis.start()
In Go:
container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: testcontainers.ContainerRequest{
Image: "redis:5.0.3-alpine",
ExposedPorts: []string{"6379/tcp"},
WaitingFor: wait.ForLog("Ready to accept connections"),
},
Started: true,
})
Testcontainers Cloud
In addition to these widely established open source libraries, Testcontainers also provides a product that seamlessly offloads these containers to the cloud without requiring any changes to your Testcontainers code: Testcontainers Cloud. By leveraging Testcontainers Cloud, you can significantly reduce the load on your local machine, freeing up resources for other tasks while still running complex, resource-intensive tests.
This approach speeds up your development workflow and brings architectural parity to your test environment with the required Docker runtime (e.g., x86) because containers are executed in a consistent and scalable cloud environment. Whether you are dealing with heavy workloads or just want to streamline your testing process, Testcontainers Cloud provides seamless integration, improving both performance and reliability, allowing you to focus more on coding and less on managing local resources.
Summary
Testcontainers is a versatile and powerful tool that changes the way developers approach integration testing and local development. By providing an easy-to-use interface to launch Docker containers tailored for specific testing needs, and accessible directly from the familiarity of the programming language used, Testcontainers eliminates the common challenges associated with managing test environments.
With modules that encapsulate best practices, automatic cleanup to keep your system tidy, and the ability to offload container execution to Testcontainers Cloud, this approach provides a comprehensive solution for maintaining consistency, reliability, and efficiency in your testing process.
Whether you are a developer looking to streamline your local workflow or a team aiming to scale testing in the cloud, Testcontainers provides you with the necessary tools to ensure your code runs seamlessly across different environments. By adopting Testcontainers, you can not only improve the quality of your tests, but also pave the way for a more powerful and confident development cycle.