I just announced the new Spring Boot 2 material, coming in REST With Spring:

>> CHECK OUT THE COURSE

Table of Contents

1. Overview

There are many ways to test the Service Layer of an application. The goal of this article is to show one way of unit testing this layer in isolation, by mocking out the interactions with the database entirely.

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

2. The Layers

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

1.1. The Service Layer

@Service
public class FooService implements IFooService{

   @Autowired
   IFooDAO dao;

   @Override
   public Long create( Foo entity ){
      return this.dao.create( entity );
   }

}

1.2. The DAL/DAO Layer

@Repository
public class FooDAO extends HibernateDaoSupport implements IFooDAO{

   public Long create( Foo entity ){
      Preconditions.checkNotNull( entity );

      return (Long) this.getHibernateTemplate().save( entity );
   }

}

3. 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, we can grow the scope of the unit test by changing the definition of the unit – we can look at a persistent operation as a unit, from the Service Layer through the DAO and all the way day to the raw persistence – whatever that is. Now, the unit test will consume the API of the Service Layer and will have the raw persistence mocked out – in this case, the HibernateTemplate:

public class FooServiceUnitTest{

   FooService instance;

   private HibernateTemplate hibernateTemplateMock;

   @Before
   public void before(){
      this.instance = new FooService();
      this.instance.dao = new FooDAO();
      this.hibernateTemplateMock = mock( HibernateTemplate.class );
      this.instance.dao.setHibernateTemplate( this.hibernateTemplateMock );
   }

   @Test
   public void whenCreateIsTriggered_thenNoException(){
      // When
      this.instance.create( new Foo( "testName" ) );
   }

   @Test( expected = NullPointerException.class )
   public void whenCreateIsTriggeredForNullEntity_thenException(){
      // When
      this.instance.create( null );
   }

   @Test
   public void whenCreateIsTriggered_thenEntityIsCreated(){
      // When
      Foo entity = new Foo( "testName" );
      this.instance.create( entity );

      // Then
      ArgumentCaptor< Foo > argument = ArgumentCaptor.forClass( Foo.class );
      verify( this.hibernateTemplateMock ).save( argument.capture() );
      assertThat( entity, is( argument.getValue() ) );
   }

}

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.

4. Conclusion

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.

I just announced the new Spring Boot 2 material, coming in REST With Spring:

>> CHECK OUT THE LESSONS

newest oldest most voted
Notify of
Steve Mosley
Guest

Nice blog, like the pragmatic approach rather than the dogmatic view that “unit tests must test only one class” … which I keep having to beat out of people. Need to make sure tests provide value, and break when mistakes are made … rather than just being re-written each time you make a code change. But just to touch on one thing that follows from my previous point … do you have separate tests that prove your DAO/DAL layer acts as expected? Just my experience, is that the parts where we have bugs are where hibernate doesn’t work as expected… Read more »

Eugen Paraschiv
Guest

You touch on some interesting points here – yes, the concept of test per class is a good constraint to follow very early on, and a good tool to learn. Moving forward, my personal preference is to look to the behaviour as being the unit, not the class.
Regarding integration testing, yes, it’s indispensable – these unit tests will not exercise the persistence layer and are not meant (or fit) to catch the particular class of problems, which is why you need integration tests as well.
Thanks for the interesting feedback.

Rafael Ponte
Guest

Well, I can’t agree with you. Writing unit tests this way doesn’t mean your tests are lesser brittle then mocking DAO layer, they still continue being brittle tests. In my experience, trying to mock third-party API’s is such a problem most of the times. For example, mocking EntityManager, Hibernate Session or even worse, the Hibernate Criteria is rarely a good idea. Normally, for service layer (that usually touches persistence layer) I write integration tests, they are more concise and most of cases simple to write, so when you have a integration tests you really don’t need to write unit ones… Read more »

Eugen Paraschiv
Guest

Well, integration tests are a different matter – these are of course necessary, but this article only discusses unit tests. I tried this approach on several projects and I didn’t see any problems with the fact that I was mocking out an artifact I didn’t own. These APIs to the raw persistence provider tend to be fairly stable and the surface interaction with them is well defined.

Thanks. Eugen.