Scala Integration Testing with TestContainers Library
Many of the times when we write Scala unit test cases, we need access to external services like databases, caches etc. Even though mocking works well for most of the cases, it’s the not the same thing. So it will be desirable to write test cases against the actual services. These test cases are known as integration test cases.
Challenge with Integration Test Cases
One of the biggest challenge with integration test case is to setup the environment correctly. For example, if we need to write test cases again Mysql we need to setup the database and connection etc. Making it available for every developer’s environment including CI (continuous Integration) is tricky. This is one of the reasons where integration test are run only in CI environments. But this discourages individual developers to write them and test them in their local environments.
Docker Based Environments
In recent years, using docker for setting up environment is becoming popular. Most of the databases, caches make their tools available as docker images. Most of the times CI tools will be setup using docker images. So if we need Mysql in CI, we will run the Mysql docker container.
This still doesn’t help in running these test cases in local machine. Expecting every developer to run the containers with right setup is not ideal. So most of these setup will be limited to CI systems.
Automating Docker Environment Setup
What if we can automate this docker based setup where the individual developer doesn’t need to worry about the same? This makes integration tests as easy as unit test cases as the developer doesn’t need to worry about setting up environments. Also now local and CI systems will behave exactly same.
That’s what testcontainers library helps to do.
Test Containers Library
TestContainers is a Java library which exposes running docker containers as test library. This exposes a simple library to run docker containers and interact with them as normal Java Library.
testcontainers-scala is a Scala port the same library which support ScalaTest integration.
In rest of the post, I will be discussing about how to use the library to run a test case running against mysql.
Mysql Integration Testing
This section of the post, we will be discussing how to run integration tests in Scala which needs Mysql.
Add Dependencies
The below dependencies should be added to build.sbt.
First dependency is the scala library with scala test integration. The second dependency is Mysql specific. There is built-in support for a lot more databases.
Mixing ForAllTestContainer Trait
There are mainly two traits to create containers. ForAllTestContainer creates a container per test suite. ForEachTestContainer creates container for each test case. As creating and destroying mysql container for each test case is costly, we will be using the former one for our test cases.
The below code is used for mixing the above trait in our test cases.
Implementing container abstract method
When we mix the ForAllTestContainer, we are required to implement container method, which defines which container to create.
The below code implements the method by creating container.
In above code, we are creating MySQLContainer. This constructor is available from the dependency we added earlier.
Using Mysql Container in Test Case
Once the container is created, we can use that instance for running test cases. The below test case create a table and runs show table.
From code we can observe that, we can connect to database using container variables like jdbcUrl so that we don’t need to hard code connection strings. This gives maximum flexibility to run database on any port which is available.
Running Test Case
When we run the test case, we can observe that it automatically pulls the image and runs a docker container for us. Before we run, let’s see the docker containers running on our machines using docker ps command.
When we run the test case, we can see the below containers by running same command
As you can see, we are running two containers. One containers is for our mysql and other one is for life cycle management. These containers automatically go away once test cases are done.
Reusing Containers in Test Suite
As we discussed earlier, if we use ForAllTestContainer container state will preserved for all test cases. We can verify by listing tables in second test case which was created in first test case.
This test case passes as the same container will be preserved.
Using Generic Container
Let’s say you may have service which doesn’t have built in support like we had for Mysql. Then what you can do?.
The library exposes a generic container API called GenericContainer which allows running any container image. So you are not restricted by the built in services. You can read more about the same here.
Code
You can access complete code here.