Enterprise Integration Zone is brought to you in partnership with:

Nicolas Frankel is an IT consultant with 10 years experience in Java / JEE environments. He likes his job so much he writes technical articles on his blog and reviews technical books in his spare time. He also tries to find other geeks like him in universities, as a part-time lecturer. Nicolas is a DZone MVB and is not an employee of DZone and has posted 228 posts at DZone. You can read more from them at their website. View Full User Profile

Spring Configuration Modularization for Integration Testing

07.28.2014
| 7129 views |
  • submit to reddit

Object-Oriented Programming advocates for modularization in order to build small and reusable components. There are however other reasons for this. In the case of the Spring framework, modularization enables Integration Testing, the ability to test the system or parts of it, including assembly configuration.

Why is it so important to test the system assembled with the final configuration? Let’s take a simple example, the making of a car. Unit Testing the car would be akin to testing every nuts and bolts of the car separately, while Integration Testing the car would be like driving it on a circuit. By testing only the car’s components separately, selling the assembled car is a huge risk as nothing guarantees it will behave correctly in real-life conditions.

Now that we have asserted Integration Testing is necessary to guarantee the adequate level of internal quality, it’s time to enable Integration Testing with the Spring framework. Integration Testing is based on the notion of SUT. Defining the SUT is to define the boundaries between what is tested and its dependencies. In nearly all cases, test setup will require to provide some kind of test double for each required dependency. Configuring those test doubles can only be achieved by modularizing Spring configuration, so that they can replace dependent beans located outside the SUT.

Sample bean dependency diagram

Fig. 1 – Sample bean dependency diagram

Spring’s DI configuration comes in 3 different flavors: XML – the legacy way, autowiring and the newest JavaConfig. We’ll have a look at how modularization can be achieved for each flavor. Mixed DI modularization can be deduced from each separate entry.

Autowiring

Autowiring is an easy way to assemble Spring applications. It is achieved through the use of either @Autowiring or @Inject. Let’s cover quickly autowiring: as injection is implicit, there’s no easy way to modularize configuration. Applications using autowiring will just have to migrate to another DI flavor to allow for Integration Testing.

XML

XML is the legacy way to inject dependencies, but is still in use. Consider the following monolithic XML configuration file:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:jee="http://www.springframework.org/schema/jee"
     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
      http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd">
  <jee:jndi-lookup id="dataSource" jndi-name="jdbc/MyDataSource" />
  <bean id="productRepository" class="ProductRepository">
    <constructor-arg ref="dataSource" />
  </bean>
  <bean id="customerRepository" class="CustomerRepository">
    <constructor-arg ref="dataSource" />
  </bean>
  <bean id="orderRepository" class="OrderRepository">
    <constructor-arg ref="dataSource" />
  </bean>
  <bean id="orderService" class="OrderService">
    <constructor-arg ref="productRepository" index="0" />
    <constructor-arg ref="customerRepository" index="1" />
    <constructor-arg ref="orderRepository" index="2" />
  </bean>
</beans>

At this point, Integration Testing orderService is not easy as it should be. In particular, we need to:

  • Download the application server
  • Configure the server for the jdbc/MyDataSource data source
  • Deploy all classes to the server
  • Start the server
  • Stop the server After the test(s)

Of course, all previous tasks have to be automated! Though not impossible thanks to tools such as Arquillian, it’s contrary to the KISS principle. To overcome this problem and make our life (as well as test maintenance) easier in the process requires tooling and design. On the tooling part, we’ll be using a local database. Usually, such a database is of the in-memory kind e.g. H2. On the design part, his requires separating our beans by creating two different configuration fragments, one solely dedicated to the data source to be faked and the other one for the beans constituting the SUT.

Then, we’ll use a Maven classpath trick: Maven puts the test classpath in front of the main classpath when executing tests. This way, files found in the test classpath will “override” similarly-named files in the main classpath. Let’s create two configuration files fragments:

The “real” JNDI datasource as in the monolithic configuration
<beans ...>
  <jee:jndi-lookup id="dataSource" jndi-name="jdbc/MyDataSource" />
</beans>
The Fake datasource
<beans...>
  <bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource">
      <property name="driverClassName" value="org.h2.Driver" />
      <property name="url" value="jdbc:h2:~/test" />
      <property name="username" value="sa" />
      <property name="maxActive" value="1" />
  </bean>
</beans>
  • Note we are using a Tomcat datasource object, this requires the org.apache.tomcat:tomcat-jdbc:jar library on the test classpath. Also note the maxActive property. This reflects the maximum number of connections to the database. It is advised to always set it to 1 for test scenarios so that connections pool exhaustion bugs can be checked as early as possible.

The final layout is the following:

Fig. 2 – Project structure for Spring XML configuration Integration Testing

  1. JNDI datasource
  2. Other beans
  3. Fake datasource

The final main-config.xml file looks like:

<?xml version="1.0" encoding="UTF-8"?>
<beans...>
  <import resource="classpath:datasource-config.xml" />
  <!-- other beans go here -->
</beans>
Such a structure is the basics to enable Integration Testing.

JavaConfig

JavaConfig is the most recent way to configure Spring applications, bringing both compile-time (as autowiring) and explicit configuration (as XML) safety.

The above datasources fragments can be “translated” in Java as follows:

  • The “real” JNDI datasource as in the monolithic configuration
@Configuration
public class DataSourceConfig {
 
    @Bean
    public DataSource dataSource() throws Exception {
        Context ctx = new InitialContext();
        return (DataSource) ctx.lookup("jdbc/MyDataSource");
    }
}
The Fake datasource
public class FakeDataSourceConfig {
 
    public DataSource dataSource() {
        org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource();
        dataSource.setDriverClassName("org.h2.Driver");
        dataSource.setUrl("jdbc:h2:~/test");
        dataSource.setUsername("sa");
        dataSource.setMaxActive(1);
        return dataSource;
    }
}
However, there are two problems that appear when using JavaConfig.
  1. It’s not possible to use the same classpath trick with an import as with XML previously, as Java forbids to have 2 (or more) classes with the same qualified name loaded by the same classloader (which is the case with Maven). Therefore, JavaConfig configuration fragments shouldn’t explicitly import other fragments but should leave the fragment assembly responsibility to their users (application or tests) so that names can be different, e.g.:
@ContextConfiguration(classes = {MainConfig.class, FakeDataSource.class})
public class SimpleDataSourceIntegrationTest extends AbstractTestNGSpringContextTests {
 
    @Test
    public void should_check_something_useful() {
        // Test goes there
    }
}
  • The main configuration fragment uses the datasource bean from the other configuration fragment. This mandates for the former to have a reference on the latter. This is obtained by using the @Autowired annotation (one of the few relevant usage of it).
  • @Configuration
    public class MainConfig {
     
        @Autowired
        private DataSource dataSource;
     
        // Other beans go there. They can use dataSource!
    }

    Summary

    In this article, I showed how Integration Testing to a Fake data source could be achieved by modularizing the monolithic Spring configuration into different configuration fragments, either XML or JavaConfig.

    However, the realm of Integration Testing – with Spring or without, is vast. Should you want to go further, I’ll hold a talk on Integration Testing at Agile Tour London on Oct. 24th and at Java Days Kiev on Oct. 17th-18th.

    This article is an abridged version of a part of the Spring chapter of Integration Testing from the Trenches. Have a look at it, there’s even a sample free chapter!

    Published at DZone with permission of Nicolas Frankel, author and DZone MVB. (source)

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

    Comments

    Waldemar Biller replied on Mon, 2014/07/28 - 4:20am

    You don't want to do this? Following this approach you are not testing your actual system, but something similar. Your actual database might behave different than your test database. Maybe you used some vendor specific functionality. How do you test this with another database system?

    Faking the data source is fine, but only to avoid JNDI in while testing. Always use the same DMBS as in your production environment.

    Nicolas Frankel replied on Mon, 2014/07/28 - 6:14am in response to: Waldemar Biller

    What prevents you from using your own personal Oracle schema? This is not the point of this article.

    Comment viewing options

    Select your preferred way to display the comments and click "Save settings" to activate your changes.