DZone
Java Zone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
  • Refcardz
  • Trend Reports
  • Webinars
  • Zones
  • |
    • Agile
    • AI
    • Big Data
    • Cloud
    • Database
    • DevOps
    • Integration
    • IoT
    • Java
    • Microservices
    • Open Source
    • Performance
    • Security
    • Web Dev
DZone > Java Zone > How to use Mock/Stub in Spring Integration Tests

How to use Mock/Stub in Spring Integration Tests

Hippoom Zhou user avatar by
Hippoom Zhou
·
Apr. 03, 13 · Java Zone · Interview
Like (1)
Save
Tweet
50.14K Views

Join the DZone community and get the full member experience.

Join For Free

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 C:Dynamic 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)

Testing Spring Framework Spring Integration Integration

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • After COVID, Developers Really Are the New Kingmakers
  • Top 20 Git Commands With Examples
  • Upsert in SQL: What Is an Upsert, and When Should You Use One?
  • How to Leverage Method Chaining To Add Smart Message Routing in Java

Comments

Java Partner Resources

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • MVB Program
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends:

DZone.com is powered by 

AnswerHub logo