In the 9 years of running Baeldung, we've never been through anything like this pandemic

And, if making my courses more affordable for a while is going to help you stay in business, land a new job, make rent or be able to provide for your family - then it's well worth doing.

Effective immediately, all Baeldung courses are 33% off their normal prices!

You'll find all three courses in the menu, above.

In the 9 years of running Baeldung, we've never been through anything like this pandemic

And, if making my courses more affordable for a while is going to help you stay in business, land a new job, make rent or be able to provide for your family - then it's well worth doing.

Effective immediately, all Baeldung courses are 33% off their normal prices!

You'll find all three courses in the menu, above.

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.