Test as you run (Part 3) – Interactions with Docker Containers
In the previous part of this series about test-driven development of microservices we tested our system with mocks of different sizes. This is the third part.
The first can be found here.
Let’s check that interactions with a real database work.
Please checkout the next step:
git checkout step4
Looking into the root build.gradle, we can see that the Docker-compose Gradle-plugin was added to the buildscript classpath:
We can see that a task is defined in lines 10–25 and we will have a look on the task in a moment, but first let’s have a look at the lines 27–36.
At first the Docker-compose plugin is applied. Following this a custom dockerCompose-Task is created named “debugWithCassandra” with the docker-compose file “docker-compose-debug.yml”.
The task is set as a requirement for the custom task defined above, therefore the Docker-containers will be up and running before the custom tasks get executed.
The Docker-compose file is located in the project and contains the following definitions:
In that file a container named “cassandra” is defined using the Spotify Cassandra image and the containers port 9042 is exposed to the host system.
Now let’s have a look at the custom task “bootRunDebugWithCassandra”:
This task is a special version of the bootRun task provided by the Gradle- Spring-plugin, therefore the task will start the Spring-boot-application.
The lines 12, 14 and 15 are just default definitions necessary to start the Spring application, so let’s have a look at the lines 17–24:
We use the Docker-compose plugin to get the IP-Address of the cassandra-container as well as the port and write these information as well as a keyspacename into the environment (lines 22–24).
Since we define these lines to be executed directly before the actual task get’s executed the UserService will be started with the environment variables in place.
All these things may sound a little bit confusing, therefore here‘s a little sum up:
- The bootRunDebugWithCassandra-task get’s called
- since the Docker-compose-task is required by this task it gets executed before the actual bootRunDebugWithCassandra-task and starts all the docker-container defined in the provided docker-compose file (which in our case is just the cassandra container)
- the IP-Address as well as the port are read from the docker container and together with a keyspace name written into the enviroment
- the actual bootRunDebugWithCassandra-task is executed and the server is started with the environment variables in place
That’s quite awesome right?
How does this help me?
By using these settings it becomes incredibly easy to start or even debug our UserServer with a Cassandra in place.
To do so, we add a run configuration in Intellij Idea:
- We click on our Run/Debug configurations
-> the green plus
- Give it a name (for example “userServiceWithCassandra”)
- select the UserService project
- select the bootRunDebugWithCassandra task
- set the checkmark for “Single instance only”
The configuration should look like this:
- press apply and close the window
Know by clicking either the run or the debug button the Gradle bootRunDebugWithCassandra-task get’s executed as described above therefore the UserService will be started with a Cassandra all the necessary information in place.
Let’s give it a try!
We run the application by just pressing the debug button and have a look into the console log in Intellij. We can see that everything described above happens:
- The Cassandra-Docker-image get’s pulled if not already available on the system
- a Cassandra container get’s created
- the UserService is started with the database in place
Now we can play a little bit with the Service.
To do so, we use Postman. Within the git repository you will find the Postman file test_as_you_run.postman_collection.json in which requests for the UserService and the CapitalizeService are defined.
Let’s create the three Users bruce wayne, martha wayne, and thomas wayne by using Postman:
This window shows how to use the createuser endpoint with the defined body values to create the User bruce wayne. By clicking the send Button the request is executed.
After adding bruce, thomas and martha, we can check the getusersbylastname endpoint to check whether the users are stored.
So now that we tried out by ourself that the UserService works as expected, lets check the database.
So we open our shell and type:
docker inspect userservice_cassandra_1 |grep IPAddress
And we get a result:
"SecondaryIPAddresses": null, "IPAddress": "",
According to the output the IP-Address of Cassandra running on my machine is 172.19.0.2; therefore if the IP-Address differs when you try it out change the next command.
We open cqlsh and check the user table:
cqlsh 172.19.0.2cqlsh> select * from test.users;
The output should look similar to this (ignore the ID-values)
lastname | id | firstname
wayne | 3cdef4c0-cab7-11e7-ab08-03b9b3e91691 | bruce
wayne | 40544560-cab7-11e7-ab08-03b9b3e91691 | martha
wayne | 432c92b0-cab7-11e7-ab08-03b9b3e91691 | thomas
Okay, so we have a Cassandra running on our machine filled with some users by the UserService. Nice!
Let’s go further and create a testdata image from this Cassandra. To do so we just create a new image from the running container by running inside of our shell
docker commit userservice_cassandra_1 usercassandra:latest
That’s all. Now we can stop the UserService and the Cassandra container will automatically get stopped as well.
Let’s use this testdata image for an integration test in part 4.