AgileWorks Romania

How to avoid brittle tests when testing the service layer

There are many ways to test the service layer of an application. The goal here is to show how to unit test this layer in isolation, by mocking out the interactions with the database entirely.

This example will use Spring 3 for the dependency injection, JUnit, Hamcrest and Mockito for testing, but the technologies can vary.

The layers

The typical java web application will havea service layer, on top of a DAL/DAO layer which in turn will calls the persistence layer.

The Service Layer:

The DAL/DAO layer:

Motivation and blurring the lines of the unit test

When unit testing a service, the standard unit is usually the service class, simple as that. The test will mock out the layer underneath - in this case the DAO/DAL layer and verify the interactions on it. Exact same thing for the DAO layer - mocking out the interactions with the database (HibernateTemplate in this example) and verifying the interactions with that.

This is a valid approach, but it leads to brittle tests - adding or removing a layer almost always means rewriting the tests entirely. This happens because the tests rely on the exact structure of the layers, and a change to that means a change to the tests.

To avoid this kind of inflexibility, the scope of the tests will grow from the single service class to all the layers. Now, when testing the service layer, it's not the layer below that will be mocked, but the final layer which is directly interacting with the database - in this case, the HibernateTemplate:

Now the test only focuses on a single responsibility - when creation is triggered, does the creation reach the database?

The last test uses Mockito verification syntax to check that the save method has been called on the hibernate template, capturing the argument in the process so that it can be checked as well. The responsibility of creating the entity is verified via this interaction test, without the need to check any state - the test trusts that the hibernate save logic is working as intended. Of course that needs to be tested as well, but that is another responsibility and another type of test.


This technique invariably leads to more focused tests, which makes them more resilient and flexible to change. The only reason the test should now fail is because the responsibility under test is broken.

Check out the original article on baeldung.

Tagged as: Comments Off
Comments (0) Trackbacks (0)

Sorry, the comment form is closed at this time.

No trackbacks yet.