During my career I often observed people who claim that they are not writing tests because can’t cover properly integration between components. Well, I believe most of such people just don’t know some simply techniques or don’t have time to dig them out because of stress at their work place. The lack of such knowledge results into neglecting of integration tests and thus worse software, more bugs and disappointed customer. So I’ve decided to share some practices revealing mystery surrounding integration testing.

Better integration test for Spring based projects

Tools: Spring, JUnit, Mockito

Imagine the situation with Spring based project that’s integrating an external service, e.g. some bank web services. Issues related to writing test cases and running them within CI for such code are usually the same

  • price per transaction, each time test executed - customer pays

  • tests requests can be interpreted as suspicious traffic, account blocked - tests failures

  • when using non production environments for tests, it can be unstable - again, tests failures

Usually such problems can be solved by mocking such external service while testing single class that using it, but when it comes to the need for test larger business flow - you need to run your tests again many components and made them managed by your container - Spring. Luckily Spring has great test framework allowing injection of beans from production configuration, but one needs to mock external services himself. First intention could be creating mocks in setUp section of the test and re-inject beans previously injected by Spring, but think more about it

You’re overriding behavior of container for your test, so there’s no guarantee it will work the same way for real services on real environment.

Instead we have not to mock our external services and then re-inject them into corresponding beans, but make Spring inject mocks in place of particular beans , required for our test goals. Let’s illustrate this with code.

My sample project comprises BankService representing external service and UserBalanceService - our service working with BankService. UserBalanceService is quite simple wrapper - just making conversion from String to Double.

BankService.java
public interface BankService {
    String getBalanceByEmail(String email);
}
BankServiceImpl.java
public class BankServiceImpl implements BankService {
    @Override
    public String getBalanceByEmail(String email) {
        throw new UnsupportedOperationException("Operation failed due to external exception");
    }
}
UserBalanceService.java
interface UserBalanceService {
    Double getAccountBalance(String email);
}
UserBalanceServiceImpl.java
public class UserBalanceServiceImpl implements UserBalanceService {
    @Autowired
    private BankService bankService;
    @Override
    public Double getAccountBalance(String email) {
        return Double.valueOf(bankService.getBalanceByEmail(email));
    }
}

And the Spring dependency XML configuration wiring everything together

applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="bankService" class="ua.eshepelyuk.blog.springtest.springockito.BankServiceImpl"/>
    <bean id="userBalanceService" class="ua.eshepelyuk.blog.springtest.springockito.UserBalanceServiceImpl"/>
</beans>

Our test will look like

UserBalanceServiceImplTest.java
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:/springtest/springockito/applicationContext.xml")
public class UserBalanceServiceImplProfileTest {
    @Autowired
    private UserBalanceService userBalanceService;
    @Autowired
    private BankService bankService;
    @Test
    public void shouldReturnMockedBalance() {
        Double balance = userBalanceService.getAccountBalance("user@bank.com");
        assertEquals(balance, Double.valueOf(123.45D));
    }
}

As expected after test run we will have UnsupportedOperationException. Our intention is to replace BankService with mock and tune its behavior. It’s possible to use Mockito directly as factory bean but there’s better alternative - Springockito framework. Please take a look before proceed :)

The remaining question is how to instruct Spring to inject mocks instead of real beans, Prior to version 3.1 there were no alternatives except creating brand new XML configuration for using it in test. But with introduction of Bean Definition Profiles we now able to create more elegant solution for this, although we still need separate XML for test purposes. This is how resulting test XML configuration will look like

testApplicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mockito="http://www.mockito.org/spring/mockito"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.mockito.org/spring/mockito https://bitbucket.org/kubek2k/springockito/raw/tip/springockito/src/main/resources/spring/mockito.xsd">
    <import resource="classpath:/springtest/springockito/applicationContext.xml"/>
    <beans profile="springTest">
        <mockito:mock id="bankService" class="ua.eshepelyuk.blog.springtest.springockito.BankService"/>
    </beans>
</beans>

And the test modified accordingly.

UserBalanceServiceImplProfileTest.java
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:/springtest/springockito/testApplicationContext.xml")
@ActiveProfiles(profiles = {"springTest"})
public class UserBalanceServiceImplProfileTest {
    @Autowired
    private UserBalanceService userBalanceService;
    @Autowired
    private BankService bankService;
    @Before
    public void setUp() throws Exception {
        Mockito.when(bankService.getBalanceByEmail("user@bank.com")).thenReturn(String.valueOf(123.45D));
    }
    @Test
    public void shouldReturnMockedBalance() {
        Double balance = userBalanceService.getAccountBalance("user@bank.com");
        assertEquals(balance, Double.valueOf(123.45D));
    }
}

You may notice appearance of setUp method for setting up the mock behavior and new @Profile annotation. The annotation activates our profile springTest so bean mocked with Springockito will be injected where necessary. On running this the test will pass, because Spring injected Mockito mock that we’ve configured in test XML and not the external service instance.

Don’t stop on the way to perfectness

It could be the end of the story be we could still go deeper on the problem. Springockito creator has another framework Springockito Annotations. The framework allows mock injection using annotation within test classes. Please skim read it before proceed :) After some modification code of our test will look this way.

UserBalanceServiceImplAnnotationTest
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = SpringockitoContextLoader.class,
    locations = "classpath:/springtest/springockito/applicationContext.xml")
public class UserBalanceServiceImplAnnotationTest {
    @Autowired
    private UserBalanceService userBalanceService;
    @Autowired
    @ReplaceWithMock
    private BankService bankService;
    @Before
    public void setUp() throws Exception {
        Mockito.when(bankService.getBalanceByEmail("user@bank.com")).thenReturn(String.valueOf(valueOf(123.45D)));
    }
    @Test
    public void shouldReturnMockedBalance() {
        Double balance = userBalanceService.getAccountBalance("user@bank.com");
        assertEquals(balance, valueOf(123.45D));
    }
}

Please note that no new XML configuration required. We’re using production XML config and just override single bean using @ReplaceWithMock annotation. Later we can customize the mock in setUp method.

P.S.

Springockito-annotations project has one great advantage - it provides test code only based dependency override mechanism. Neither additional XML, nor production code modifications for test purposes. Unlike springockito-annotations approach the XML based one makes creation of test specific XML mandatory always. So I strongly recommend using Springockito-annotations project for your integration tests, so they won’t affect your production code design and won’t produce additional artifacts - i.e. test XML configuration files.

P.P.S.

Writing integration tests for Spring is easy ! Project can be found on My GitHub