A common problem when doing end-to-end tests is colliding ports on you buildmachine with parallel execution. With the technology stack of Docker, Jenkins and Gradle I’ll demonstrate one solution I use in my current project to start the backend with a random port and use it in the test execution afterwards.
Our situation is the following: You want to execute a test which starts your backend application and fires against it from the outside. That way you can make sure that your whole infrastructure of the backend is working, including the servlet container for example. But the problem lies in the small detail that such tests usually take a while to execute and therefore lengthen your job runtime while blocking a single port over the time of execution. So the simplest solution by just starting the backend on a fixed port does not scale well for larger teams where a bunch of jobs are executed all the time. Therefore we’d think about starting the backend with randomly assigned ports. The graphic below demonstrates what’s happening on a single executor on Jenkins.
The whole example is available on GitHub
Our example backend is a very simple service written with Spring Boot and Kotlin which has one endpoint we want to test against:
The test should happen through another application, because we want to test against the actual
jar of our backend. So we create another application, called integration-test that only consists of a single unit test written with JUnit in Java:
As you can see the test is quite simple and performs a
GET request against the
/example endpoint we defined earlier to check if the expected body is received. The hard problem to solve lies in the
backendUrl variable. We need to point the test to the correct address with the correct port the server started on. For local testing this is easy, it’s just the configured port of the backend application, but as soon as we want to execute the test on Jenkins or any other buildmachine we want to be independent of the port to avoid conflicts with other builds.
So first we need the functionality to start the backend with a random port. For that purpose we use
docker-compose. The configuration is simple again:
By configuring the service with
docker-compose will take an available port for us to expose and maps it on the port
8080 inside of the container, which is the port we configured for our backend application.
Starting our setup with
docker-compose up shows us that we assign a random port.
As a next step we need to pass this port to our
integration-test. To do so we use the docker-compose plugin from avast. With it we have the possibility to start our container with gradle and receive information about it afterwards which we can pass through system properties to the integration-test.
Within our integration-test we can read the system properties and assign it to a field which is accessible in our test:
And that is it already. When we execute
./gradlew integration-test:test we start the docker container with a random port and the test picks it up. This can be easily executed on any build machine without interfering other builds.
When you want to run a setup like this I recommend configuring an additional cronjob that cleans up dangling containers from time to time. It can happen that for some random reason a container might not stop properly.