Friday, August 9, 2013

Injecting Test Doubles in Spring using Mockito and BeanPostProcessors


I'm pretty sure that if you have ever used Spring and are familliar with unit testing, you have encountered a problem related to injecting mocks / spies (Test Doubles) in the Spring's application context which you wouldn't want to modify. This article presents an approach how to solve this issue using Spring's components.

Project structure

Let's start with the project structure:


As usual to present a problem I'm trying to show a very simple project structure. The approach that I'm about to show could show more benefits if I made the problem more extensive as we had in our project:

  • we had dozens of interfaces and implementations autowired to lists
  • we wanted to perform some functional tests basing on the existing Spring application context 
  • we wanted to verify that for certain input conditions some specific implementation would have their methods executed 
  • we wanted to stub database access.

In this example we have a PlayerService that gets a Player using a PlayerWebService. We have an applicationContext that simply defines packages for autowiring:

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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <context:component-scan base-package="com.blogspot.toomuchcoding"/>

</beans>

Then we have our very simple model:

Player.java

package com.blogspot.toomuchcoding.model;

import java.math.BigDecimal;

/**
 * User: mgrzejszczak
 * Date: 08.08.13
 * Time: 14:38
 */
public final class Player {
    private final String playerName;
    private final BigDecimal playerValue;

    public Player(final String playerName, final BigDecimal playerValue) {
        this.playerName = playerName;
        this.playerValue = playerValue;
    }

    public String getPlayerName() {
        return playerName;
    }

    public BigDecimal getPlayerValue() {
        return playerValue;
    }
}


the implementation of the PlayerService that uses PlayerWebService to retrieve data regarding the Player:

PlayerServiceImpl.java

package com.blogspot.toomuchcoding.service;

import com.blogspot.toomuchcoding.model.Player;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * User: mgrzejszczak
 * Date: 08.06.13
 * Time: 19:02
 */
@Service
public class PlayerServiceImpl implements PlayerService {
    private static final Logger LOGGER = LoggerFactory.getLogger(PlayerServiceImpl.class);

    @Autowired
    private PlayerWebService playerWebService;

    @Override
    public Player getPlayerByName(String playerName) {
        LOGGER.debug(String.format("Logging the player web service name [%s]", playerWebService.getWebServiceName()));
        return playerWebService.getPlayerByName(playerName);
    }

    public PlayerWebService getPlayerWebService() {
        return playerWebService;
    }

    public void setPlayerWebService(PlayerWebService playerWebService) {
        this.playerWebService = playerWebService;
    }
}


the implementation of the PlayerWebService that is a provider of data (in this scenario we are simulating awaiting for response):

PlayerWebServiceImpl.java

package com.blogspot.toomuchcoding.service;

import com.blogspot.toomuchcoding.model.Player;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;

/**
 * User: mgrzejszczak
 * Date: 08.08.13
 * Time: 14:48
 */
@Service
public class PlayerWebServiceImpl implements PlayerWebService {
    private static final Logger LOGGER = LoggerFactory.getLogger(PlayerWebServiceImpl.class);
    public static final String WEB_SERVICE_NAME = "SuperPlayerWebService";
    public static final String SAMPLE_PLAYER_VALUE = "1000";

    @Override
    public String getWebServiceName() {
        return WEB_SERVICE_NAME;
    }

    @Override
    public Player getPlayerByName(String name) {
        try {
            LOGGER.debug("Simulating awaiting time for a response from a web service");
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            LOGGER.error(String.format("[%s] occurred while trying to make the thread sleep", e));
        }
        return new Player(name, new BigDecimal(SAMPLE_PLAYER_VALUE));
    }
}

Perhaps the project structure and the methods are not one of the most brilliant you have ever seen but I wanted to keep it simple to present the problem ;)

The problem

So what actually is the problem? Let us assume that we want our autowired PlayerWebServiceImpl to be a Spy that we can verify. What is more you don't want to actually change anything in the applicationContext.xml - you want to use the current version of the Spring context.

With mocks it's easier since you can define in your XML file (using Mockito factory method) your bean as a mock to override the original implementation just like this:

    <bean id="playerWebServiceImpl" class="org.mockito.Mockito" factory-method="mock">
        <constructor-arg value="com.blogspot.toomuchcoding.service.PlayerWebServiceImpl"/>
    </bean>

What about the Spy? It's more problematic since in order to create a Spy you need an already existing object of the given type. In our example we have some autowiring going on so we would have to first create a spring bean of the PlayerWebService type (Spring would have to wire all its dependencies) and then wrap it around with Mockito.spy(...) and only then would it have to be wired somewhere else... It's getting very complicatied doesn't it?

The solution

You can see that the problem is not that trivial to be solved. An easy way to fix it however is to use native Spring mechanisms - BeanPostProcessors. You can check my article about how to create a Spring BeanPostProcessor for a specified type - we'll be using it in this example.

Let's start with checking the test class:

PlayerServiceImplTest.java

package com.blogspot.toomuchcoding.service;

import com.blogspot.toomuchcoding.model.Player;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.math.BigDecimal;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.BDDMockito.doReturn;
import static org.mockito.Mockito.verify;

/**
 * User: mgrzejszczak
 * Date: 08.06.13
 * Time: 19:26
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:testApplicationContext.xml")
public class PlayerServiceImplTest {

    public static final String PLAYER_NAME = "Lewandowski";
    public static final BigDecimal PLAYER_VALUE = new BigDecimal("35000000");

    @Autowired
    PlayerWebService playerWebServiceSpy;

    @Autowired
    PlayerService objectUnderTest;

    @Test
    public void shouldReturnAPlayerFromPlayerWebService(){
        //given
        Player referencePlayer = new Player(PLAYER_NAME, PLAYER_VALUE);
        doReturn(referencePlayer).when(playerWebServiceSpy).getPlayerByName(PLAYER_NAME);

        //when
        Player player = objectUnderTest.getPlayerByName(PLAYER_NAME);

        //then
        assertThat(player, is(referencePlayer));
        verify(playerWebServiceSpy).getWebServiceName();
        assertThat(playerWebServiceSpy.getWebServiceName(), is(PlayerWebServiceImpl.WEB_SERVICE_NAME));
    }


}

In this test we want to mock retrieval of Player from the PlayerWebService (let's assume that normally it would try to send a request to the outside world - and we wouldn't want that to happen in our scenario) and test that our PlayerService returns the Player that we provided in the method stub and what is more we want to perform verification on the Spy that the method getWebServiceName() has been executed and that it has a very precisely defined return value. In other words we wanted to stub the method getPlayerByName(...) and wanted to perform verification of the spy by checking the getWebServiceName()method.

Let's check the test context:

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <import resource="applicationContext.xml"/>
    <bean class="com.blogspot.postprocessor.PlayerWebServicePostProcessor" />
</beans>

The test context is very small since it's importing the current applicationContext.xml and creating a Bean that is the key feature in this example - the BeanPostProcessor:

PlayerWebServicePostProcessor.java

package com.blogspot.postprocessor;


import com.blogspot.toomuchcoding.processor.AbstractBeanPostProcessor;
import com.blogspot.toomuchcoding.service.PlayerWebService;

import static org.mockito.Mockito.spy;

/**
 * User: mgrzejszczak
 * Date: 07.05.13
 * Time: 11:30
 */
public class PlayerWebServicePostProcessor extends AbstractBeanPostProcessor<PlayerWebService> {
    public PlayerWebServicePostProcessor() {
        super(PlayerWebService.class);
    }

    @Override
    public PlayerWebService doBefore(PlayerWebService bean) {
        return spy(bean);
    }

    @Override
    public PlayerWebService doAfter(PlayerWebService bean) {
        return bean;
    }
}

The class is extending the AbstractBeanPostProcessor that implements the BeanPostProcessor interface. The logic behind this class is to register the Class for which one wants to perform some actions either before initialization (postProcessBeforeInitialization) or after initialization of the bean (postProcessAfterInitialization). The AbstractBeanPostProcessor is well explained in my post Spring BeanPostProcessor for a specified type but there is one slight change - in my old post we were allowed by the abstraction to perform some actions on the bean without the possibility of returning a wrapper or a proxy on the bean.

As you can see in the case of PlayerWebServicePostProcessor before initialization we are creating a Spy using Mockito.spy(...) method. In this way we create a factory hook on the intialization of beans of given type - it's as simple as that. This method will be executed for all the classes that implement the PlayerWebService interface.

Other possibilities

While checking out current solutions to this problem I have encountered the Springockito library by Jakub Janczak.

I haven't been using this so I don't know what are (if there are any ;) ) production issues related to this library but it seems really nice and intuitive - great job Jakub! Still, you become dependent on the external library whereas in this example I've shown how to deal with the issue using Spring.

Summary

In this post I've shown how to
  • create mocks for existing beans using XML Spring configuration
  • create a BeanPostProcessor implementation that performs logic for a given class of beans
  • return Spy (you could also return a Mock) for the given class of bean
Now let's move through the Prons and Cons of my approach:

Advantages
  • you use Spring native mechanism to create Test Doubles for your beans
  • you are not required to add any additional external dependencies
  • if you use the AbstractBeanPostProcessor you have very little changes to implement
Disadvantages
  • you have to be familliar with internal Spring architecture (that it uses BeanPostProcessors) - but is it a disadvantage? ;) - in fact if you use the AbstractBeanPostProcessor  you don't have to be familliar with it - you just have to provide the class type and actions to happen before and after initialization.
  • it's less intuitive than annotations like in the Springockito library

Sources

The sources are available at TooMuchCoding BitBucket repository and TooMuchCoding Github repository.

2 comments:

  1. I understand what's you're problem and I found you solution quite elegant. Nevertheless, I kind of disagree with your method.

    I think the point is that you want to mock something using Spring. Do you really need an inversion of control for your unit tests? Isn't it a little bit overkill ?

    If you're writing a unit test, your goal is to be the more independent as possible of other pieces of code so you are sure the bug is in your lines of code. A simple call to new MyObjet() and the needed setters are simple and efficients. Just do the injection yourself, it will not cost you much and most of the time it will be more readable than a BeanPostProcessors doing black magic somewhere.

    If you're writing integration test, then why do you want to mock anything ?

    I think you should keep your unit test as simple and stupids as possible (KISS principle). As Martin Golding said : “Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.”

    ReplyDelete
    Replies
    1. Hi! Thank you very much for your opinion! I'm really into having such discussions :)

      I would like to be very clear that this solution is not an overkill if you are trying to perform a functional test of your application. Apart from this you should of course have unit tests that test your single piece of application in isolation.

      As for the integration tests it is obvious that you don't want to mock the parts that call the outer world. In this example I just wanted to show that I actually managed to stub the execution - to prove that the post processors work.

      In our project we created parametrized tests that took plenty of stored test resources, the bean post processors converted beans into spies and basing on the test scenario *verified* whether a proper method got executed. Contrary to the unit test this test actually takes the real application context that we have in the main part of the code and simulates the real flow of the application. Having this in mind - you manage to test the bigger picture.

      To explain it even better imagine that you wrote your unit tests in isolation and the tests pass (you're testing a functionality of a single bean). What if someone forgets to add this bean (or removes it) to XML Spring config? The functionality will not be operational even though the test passes.

      I hope that I managed to clear this separation out because I might have not presented as clear as I wanted. Looking at the problem like this IOC is definitely not an overkill and the parametrized test shows "the guy who ends up maintaining your code" in a very clear manner how exactly the code works. I think that even if he was a psychopath he wouldn't want to kill me :D Hopefully :P

      Cheers and thank you very much yet again for your comment.

      Delete