- If you're ready for something different, I recommend this Linux distro to newbies and experts alike
- I finally found a Bluetooth speaker that's not afraid to get loud (and still sounds good)
- I tested the lightest 16-inch convertible laptop of the year, but that's not even its best feature
- The smartwatch with the best battery I've tested is still just $50 after the holidays
- I found a creator's laptop that competes with the MacBook Pro (and more affordable)
Streamlining Local Development with Dev Containers and Testcontainers Cloud | Docker
In today’s world of fast-paced development, setting up a consistent and efficient development environment for all team members is crucial for overall productivity. Although Docker itself is a powerful tool that enhances developer efficiency, configuring a local development environment still can be complex and time-consuming. This is where development containers (or dev containers) come into play.
Dev containers provide an all-encompassing solution, offering everything needed to start working on a feature and running the application seamlessly. In specific terms, dev containers are Docker containers (running locally or remotely) that encapsulate everything necessary for the software development of a given project, including integrated development environments (IDEs), specific software, tools, libraries, and preconfigured services.
This description of an isolated environment can be easily transferred and launched on any computer or cloud infrastructure, allowing developers and teams to abstract away the specifics of their operating systems. The dev container settings are defined in a devcontainer.json
file, which is located within a given project, ensuring consistency across different environments.
However, development is only one part of a developer’s workflow. Another critical aspect is testing to ensure that code changes work as expected and do not introduce new issues. If you use Testcontainers for integration testing or rely on Testcontainers-based services to run your application locally, you must have Docker available from within your dev container.
In this post, we will show how you can run Testcontainers-based tests or services from within the dev container and how to leverage Testcontainers Cloud within a dev container securely and efficiently to make interacting with Docker even easier.
Getting started with dev containers
To get started with dev containers on your computer using this tutorial, you will need:
- Git 2.25+
- Docker
- IntelliJ IDE
There’s no need to preconfigure your project to support development containers; the IDE will do it for you. But, we will need some Testcontainers usage examples to run in the dev container, so let’s use the existing Java Local Development workshop repository. It contains the implementation of a Spring Boot-based microservice application for managing a catalog of products. The demo-state
branch contains the implementation of Testcontainers-based integration tests and services for local development.
Although this project typically requires Java 21 and Maven installed locally, we will instead use dev containers to preconfigure all necessary tools and dependencies within the development container.
Setting up your first dev container
To begin, clone the project:
git clone https://github.com/testcontainers/java-local-development-workshop.git
Next, open the project in your local IntelliJ IDE and install the Dev Containers plugin (Figure 1).
Next, we will add a .devcontainer/devcontainer.json
file with the requirements to the project. In the context menu of the project root, select New > .devcontainer (Figure 2).
We’ll need Java 21, so let’s use the Java Dev Container Template. Then, select Java version 21 and enable Install Maven (Figure 3).
Select OK, and you’ll see a newly generated devcontainer.json
file. Let’s now tweak that a bit more.
Because Testcontainers requires access to Docker, we need to provide some access to Docker inside of the dev container. Let’s use an existing Development Container Feature to do this. Features enhance development capabilities within your dev container by providing self-contained units of specific container configuration including installation steps, environment variables, and other settings.
You can add the Docker-in-Docker feature to your devcontainer.json
to install Docker into the dev container itself and thus have a Docker environment available for the Testcontainers tests.
Your devcontainer.json
file should now look like the following:
{
"name": "Java Dev Container TCC Demo",
"image": "mcr.microsoft.com/devcontainers/java:1-21-bullseye",
"features": {
"ghcr.io/devcontainers/features/java:1": {
"version": "none",
"installMaven": "true",
"installGradle": "false"
},
"docker-in-docker": {
"version": "latest",
"moby": true,
"dockerDashComposeVersion": "v1"
}
},
"customizations" : {
"jetbrains" : {
"backend" : "IntelliJ"
}
}
}
Now you can run the container. Navigate to devcontainer.json
and click on the Dev Containers plugin and select Create Dev Container and Clone Sources. The New Dev Container window will open (Figure 4).
In the New Dev Container window, you can select the Git branch and specify where to create your dev container. By default, it uses the local Docker instance, but you can select the ellipses (…) to add additional Docker servers from the cloud or WSL and configure the connection via SSH.
If the build process is successful, you will be able to select the desired IDE backend, which will be installed and launched within the container (Figure 5).
After you select Continue, a new IDE window will open, allowing you to code as usual. To view the details of the running dev container, execute docker ps
in the terminal of your host (Figure 6).
If you run the TestApplication class, your application will start with all required dependencies managed by Testcontainers. (For more implementation details, refer to the “Local development environment with Testcontainers” step on GitHub.) You can see the services running in containers by executing docker ps
in your IDE terminal (within the container). See Figure 7.
Setting up Testcontainers Cloud in your dev container
To reduce the load on local resources and enhance the observability of Testcontainers-based containers, let’s switch from the Docker-in-Docker feature to the Testcontainers Cloud (TCC) feature: ghcr.io/eddumelendez/test-devcontainer/tcc:0.0.2
.
This feature will install and run the Testcontainers Cloud agent within the dev container, providing a remote Docker environment for your Testcontainers tests.
To enable this functionality, you’ll need to obtain a valid TC_CLOUD_TOKEN
, which the Testcontainers Cloud agent will use to establish the connection. If you don’t already have a Testcontainers Cloud account, you can sign up for a free account. Once logged in, you can create a Service Account to generate the necessary token (Figure 8).
To use the token value, we’ll utilize an .env
file. Create an environment file under .devcontainer/devcontainer.env
and add your newly generated token value (Figure 9). Be sure to add devcontainer.env
to .gitignore
to prevent it from being pushed to the remote repository.
In your devcontainer.json
file, include the following options:
- The
runArgs
to specify that the container should use the.env
file located at.devcontainer/devcontainer.env
. - The
containerEnv
to set the environment variablesTC_CLOUD_TOKEN
andTCC_PROJECT_KEY
within the container. TheTC_CLOUD_TOKEN
variable is dynamically set from the local environment variable.
The resulting devcontainer.json
file will look like this:
{
"name": "Java Dev Container TCC Demo",
"image": "mcr.microsoft.com/devcontainers/java:21",
"runArgs": [
"--env-file",
".devcontainer/devcontainer.env"
],
"containerEnv": {
"TC_CLOUD_TOKEN": "${localEnv:TC_CLOUD_TOKEN}",
"TCC_PROJECT_KEY": "java-local-development-workshop"
},
"features": {
"ghcr.io/devcontainers/features/java:1": {
"version": "none",
"installMaven": "true",
"installGradle": "false"
},
"ghcr.io/eddumelendez/test-devcontainer/tcc:0.0.2": {}
},
"customizations": {
"jetbrains": {
"backend": "IntelliJ"
}
}
}
Let’s rebuild and start the dev container again. Navigate to devcontainer.json
, select the Dev Containers plugin, then select Create Dev Container and Clone Sources, and follow the steps as in the previous example. Once the build process is finished, choose the necessary IDE backend, which will be installed and launched within the container.
To verify that the Testcontainers Cloud agent was successfully installed in your dev container, run the following in your dev container IDE terminal:
cat /usr/local/share/tcc-agent.log
You should see a log line similar to Listening address=
if the agent started successfully (Figure 10).
Now you can run your tests. The ProductControllerTest
class contains Testcontainers-based integration tests for our application. (For more implementation details, refer to the “Let’s write tests” step on GitHub.)
To view the containers running during the test cycle, navigate to the Testcontainers Cloud dashboard and check the latest session (Figure 11). You will see the name of the Service Account you created earlier in the Account line, and the Project name will correspond to the TCC_PROJECT_KEY
defined in the containerEnv
section. You can learn more about how to tag your session by project or workflow in the documentation.
If you want to run the application and debug containers, you can Connect to the cloud VM terminal and access the containers via the CLI (Figure 12).
Wrapping up
In this article, we’ve explored the benefits of using dev containers to streamline your Testcontainers-based local development environment. Using Testcontainers Cloud enhances this setup further by providing a secure, scalable solution for running Testcontainers-based containers by addressing potential security concerns and resource limitations of Docker-in-Docker approach. This powerful combination simplifies your workflow and boosts productivity and consistency across your projects.
Running your dev containers in the cloud can further reduce the load on local resources and improve performance. Stay tuned for upcoming innovations from Docker that will enhance this capability even further.