Test as you run (Part 2) – Testing with Database Mocks
In the previous part of our series about test-driven development of microservices we created unit tests for our business logic, had a look at Server-Mock tests and ended up with a Server-Stack Test. This is the second part.
The first part can be found here. Let’s go on by creating a test for a service depending on a database.
Please checkout the next step:
git checkout step3
You will find a new sub-project, which is the UserService. Users are very simple objects with just two attributes: a firstname and a lastname.
The service allows us to store users and to request all stored users with the same lastName. We use a Cassandra database to store the users. We will not talk about the actual configuration for the database, since this is very straightforward and explained in many other great tutorials.
If you’re curious about used configuration, take a look at the packagede.itemis.webengineering.blog.testasyourun.userservice.config.cassandra
Testing Every Scope
Instead, we want to focus on the testing approaches for such a setup.
At first we should take a look at the business logic,that encompasses all technology-agnostic calculations we have to do. Having a look at the UserController (which is the RestController providing the endpoints for creating a user and getting all stored users by lastName), we don’t see any special business logic there. Therefore we will ignore the unit test scope for this service and directly go on to the server tests.
The UserService uses the UserRepository, which is a Spring abstraction for Cassandra interactions. To verify the interactions with the UserRepository we can use a mock. The following snippet shows an example:
All we have to do, to use a mocked Repository (or any Bean) inside our service is to reference to it in the test and annotate it with the MockBean-annotation as shown in the lines 7 and 8. Instead of using the UserRepository as it is implemented, the mock object will be injected it into our production code and we can easily verify the interations with it as shown in the lines 19 and 30.
Checking the Database Interaction
Mocking away objects used to interact with external systems is great for testing the behavior of our service. Still, it’s not enough to make sure the externals are used in the intended way. We need tests checking interactions similar to the interactions we will have in production. So let’s create a test that actually interacts with something that behaves like the external system which in our case is a Cassandra database.
Let’s have a look at (a snippet of) the UserServiceEmbeddedTest class:
Heading to line 7 and 8, we see that the previously described TestRestTemplate is injected as well as a UserRepository.
With these injected objects we have everything in place to sent REST-calls to our Service and interact with Cassandra.
For example: We sent a REST-call to create a user (lines 15–20) and check that the user was stored inside of Cassandra (lines 22–24).
That seems nice and easy.
Where’s My Test Database?
But wait! Okay, we have everything in place to communicate with the server and with a Cassandra, but where is the Cassandra-instance? And how does the server communicate to the same Cassandra-instance as we do in the test?
To find an answer for this questions we take a look at the lines 3 and 4.
The EmbeddedCassandraTestExecutionListener is added to the TestExecutionListeners of this Test. TestExecutionListener can be used in the way the Before, BeforeClass, After, AfterClass annotations known from JUnit.
Let’s have a look at(a snippet of) the Listener:
The beforeTestClass-Method is called before any test of the test class get’s executed. As we can see in line 8, that an embedded Cassandra get started, which is a lightweight Cassandra mock that can be used for tests (for more information see CassandraUnit). To keep our tests isolated from each other, the Cassandra get’s cleared after every test (see the afterTestMethod-Method).
So now that we have an easy way to test our interaction with our database, we can go on to the next reasonable scope in our next post.