Enterprise Integration Zone is brought to you in partnership with:

Hippoom now works and lives in Shanghai as a software architect. He has six years j2ee development experience and goes in for helping his team practice DDD, TDD and continuous delivery. Hippoom has posted 1 posts at DZone. You can read more from them at their website. View Full User Profile

How to use Mock/Stub in Spring Integration Tests

04.03.2013
| 14812 views |
  • submit to reddit

Generally, you pick up a subset of components in some integration tests to check if they are glued as expected.  To achieve this,  they are usually really invoked, but sometimes, it is too expensive to do so.  For example, Component  A invokes Component B, and Component B has a dependency on an external system which does not have a test server. We really want to verify the configurations, it seems the only way is replacing Component B  with test double after wiring Component A and B.

Let's start with Strategy A: Manual Injecting

@RunWith(SpringJUnit4ClassRunner.class)  
@ContextConfiguration(locations = "classpath:config.xml")  
public class SomeAppIntegrationTestsUsingManualReplacing {  
  
    private Mockery context = new JUnit4Mockery();     (1)  
  
    private SomeInterface mock = context.mock(SomeInterface.class);   (2)  
  
    @Resource(name = "someApp")  
    private SomeApp someApp;                  (3)  
  
    @Before  
    public void replaceDependenceWithMock() {  
        someApp.setDependence(mock);          (4)  
    }  
  
    @DirtiesContext 
    @Test  
    public void returnsHelloWorldIfDependenceIsAvailable() throws Exception {  
  
        context.checking(new Expectations() {  
            {  
                allowing(mock).isAvailable();          
                will(returnValue(true));         (5)  
            }  
        });  
  
        String actual = someApp.returnHelloWorld();  
        assertEquals("helloWorld", actual);  
        context.assertIsSatisfied(); (6)  
    }  
} 


We get a spring bean someApp(Component A in this case), and it has a denpendence on SomeInterface's(Component B in this case).  We inject mock (declare and init at step 4) to someApp, thus the test passes without sending request to the external system.  The context.assertIsSatisfied()(at step 6 ) is very important as we use SpringJUnit4ClassRunner as junit runner instead of JMock, so you have to explictly assert that all expectations are satisfied.

 There are two downsides of the previous strategy:

Firstly, if there are more than one mock, you have to inject  them one by one, which is very tedious especially when you need to inject mocks into serveral spring bean.

Secondly, the wiring is not tested.  For example, if I forget to write  <property name="beanName" ref="bean" /> the integration tests using manual inject strategy is not going to tell.

Strategy B: Using predefined BeanPostProcessor

Spring provides BeanPostProcessor which is very useful when you want to replace some bean after the wiring is done.  According to the reference, application context will auto detect all BeanPostProcessor registered in metadata(usually in xml format). 

public class PredefinedBeanPostProcessor implements BeanPostProcessor {  
  
    public Mockery context = new JUnit4Mockery();    (1)  
  
    public SomeInterface mock = context.mock(SomeInterface.class);   (2)  
  
    @Override  
    public Object postProcessBeforeInitialization(Object bean, String beanName)  
            throws BeansException {  
        return bean;  
    }  
  
    @Override  
    public Object postProcessAfterInitialization(Object bean, String beanName)  
            throws BeansException {  
        if ("dependence".equals(beanName)) {  
            return mock;  
        } else {  
            return bean;  
        }  
    }  
}  

@RunWith(SpringJUnit4ClassRunner.class)  
@ContextConfiguration(locations = { "classpath:config.xml",  
        "classpath:predefined.xml" })   (1)  
public class SomeAppIntegrationTestsUsingPredefinedReplacing {  
  
    @Resource(name = "someApp")  
    private SomeApp someApp;  
  
    @Resource(name = "predefined")  
    private PredefinedBeanPostProcessor fixture;  
  
    @Test  
    public void returnsHelloWorldIfDependenceIsAvailable() throws Exception {  
  
        fixture.context.checking(new Expectations() {  
            {  
                allowing(fixture.mock).isAvailable();  
                will(returnValue(true));  
            }  
        });  
  
        String actual = someApp.returnHelloWorld();  
        assertEquals("helloWorld", actual);  
        fixture.context.assertIsSatisfied();  
    }  
}  

Notice there is an extra config xml in which the PredefinedBeanPostProcessor is registered(at step 1).  The predefined.xml is placed in src/test/resources/, so it will not be packed into the artifact for production.

 For each test, using Strategy B requires inputting both a java file and a xml which is quite verbose.

 Now we have learned the pros and cons of  Strategy A and Strategy B.  What about a hybrid version -- killing two birds with one stone.  Therefore we have the next strategy.

Strategy CDynamic Injecting 

public class TestDoubleInjector implements BeanPostProcessor {  
  
    private static Map<String, Object> MOCKS = new HashMap<String, Object>(); (1)  
  
    @Override  
    public Object postProcessBeforeInitialization(Object bean, String beanName)  
            throws BeansException {  
        return bean;  
    }  
  
    @Override  
    public Object postProcessAfterInitialization(Object bean, String beanName)  
            throws BeansException {  
        if (MOCKS.containsKey(beanName)) {  
            return MOCKS.get(beanName);  
        }  
        return bean;  
    }  
  
    public void addMock(String beanName, Object mock) {  
        MOCKS.put(beanName, mock);  
    }  
  
    public void clear() {  
        MOCKS.clear();  
    }  
  
}  

@RunWith(JMock.class)  
public class SomeAppIntegrationTestsUsingDynamicReplacing {  
  
    private Mockery context = new JUnit4Mockery();  
  
    private SomeInterface mock = context.mock(SomeInterface.class);  
  
    private SomeApp someApp;  
  
    private ConfigurableApplicationContext applicationContext;  
  
    private TestDoubleInjector fixture = new TestDoubleInjector(); (1)  
  
    @Before  
    public void replaceDependenceWithMock() {  
  
        fixture.addMock("dependence", mock);  (2)  
  
        applicationContext = new ClassPathXmlApplicationContext(new String[] {  
                "classpath:config.xml", "classpath:dynamic.xml" });  (3)  
        someApp = (SomeApp) applicationContext.getBean("someApp");  
    }  
  
    @Test  
    public void returnsHelloWorldIfDependenceIsAvailable() throws Exception {  
  
        context.checking(new Expectations() {  
            {  
                allowing(mock).isAvailable();  
                will(returnValue(true));  
            }  
        });  
  
        String actual = someApp.returnHelloWorld();  
        assertEquals("helloWorld", actual);  
    }  
  
    @After  
    public void clean() {  
        applicationContext.close();  
        fixture.clear();  
    }  
}  

The TestDoubleInjector class is an implementation of Monostate pattern. Mocks are added to the static map before the application context being created. When another TestDoubleInjector instance (defined in dynamic.xml) is initiated, it can share the static map for replacement.  Just beware to clear the static map after tests. 

By the way, you could use Stub instead of Mocks with same strategies. 

Please do not hesitate to contact me if you might have any questions.  And I do appreciate it, if you could let me know you have a better idea. Thanks!  

Resources:

http://www.jmock.org

http://www.oracle.com/technetwork/articles/entarch/spring-aop-with-ejb5-093994.html(I saw BeanPostProcessor the first time in this post)

Published at DZone with permission of its author, Hippoom Zhou.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)