Testing application without creating a mess
Problem - How many times have we faced this problem of worrying about cleaning up after running test cases. When some of our test cases run, they might add data to our database or add files to our directory which we don’t want to worry about every time.
Solution -We can easily solve this using Docker. Imagine spawning a Docker container just to run test cases and killing it after it’s purpose is solved. Poof! our problem is solved, no need to worry about the collateral damage done by our test cases since killing that container handles that for us.
What is docker?
There are many resources out there which will explain what is docker and containerization. To explain you in the context of this in layman terms, it can be considered a free computer that we get to do some stuff and you’re given to liberty to do whatever you want with it because once you’re done it will be destroyed and next time you’ll get a brand new one.
Step 1: Creating our app
I have created a simple todo app that basically allows a user to save, fetch, and delete the item. I will be exposing the REST endpoint for all these functionalities and will be using MySQL as my database.
As we see we connect to MySQL and pass the connection to our model/repository which is a dependency of our service which handles our business logic.
Above is our ListService which is essentially our controller, having logic for 3 endpoints.
This is our model handling database operations.
Step 2: Start testing it using Docker
Unit tests
First, we will start unit testing. We can directly test our models. This is where the importance of cleanup comes in. As we can see we are modifying essentially our database after every operation. Let’s say sometimes there are 100s of inserts running in each test. After every test run unnecessarily after DB will start filling.
We will use a MySQL docker container to spawn a database and pass that connection to our ListRepository. After unit testing our repo methods, we will purge the container thus avoiding the hassle of cleanup.
Starting and stopping the docker containers via code
There are essentially docker clients available for each language
Python
Java
Node
We will be using a Go client to handle docker operations.
I’ll explain what’s happening
- We create a new connection pool to docker and try connecting it.
- After the docker connection is successful we spawn a new MySQL container hosting our database.
- We try connecting to that container in a loop with a given timeout since spawning the container will take some time. If a timeout occurs then purge the container and exit.
- If the Conn.ping() doesn't return an error, congrats we have successfully connected to SQL container.
- Run the test cases and after that's done stop the SQL container.
- Voila! no hassle of cleanup our db since purging the container will take care of it.
Below is the example of our test case
Hence we have successfully tested our repository using Docker MySQL.
E2E test using Docker
Above, we are only testing our repository. What if we want to test our business logic and server as well with docker. Say if our business makes some file changes on our system. On running it’s test cases, we will also need to do the cleanup.
What we essentially need to do is start our app server in a separate docker container and run our e2e tests.
Setup
Above is a simple docker file for our application. But this docker container will not have MySQL installed with it. We need to start SQL in a separate container and bridge the network. We can do this using Docker Compose.
This Docker compose files can help us running both the service(SQL, todo_app) together and connecting them via a network bridge
We start our service using: `docker-compose up -d`
This will ensure that our server is up and running and we can do our e2e tests below.
Our server will be up on localhost:8080 and we can directly hit it.
We can test it by making HTTP requests to docker server and asserting responses.
After we have completed the test we will have to manually shutdown todo server and SQL containers using `docker-compose down`. In this case, we have to manually start and stop the docker service but this can be automated as well by using commands via code to start and stop the containers.
Hence by doing this, we ensure that if our app makes changes in our system or our database those are not persisted and we don't have to clean those up.
Conclusion:
Hence we saw using docker we can run e2e and unit test without creating a mess of our system rendered by our application. Full code available here.