Monday, June 22, 2009

Base Test Class with Maven

I've got some base Test classes (with settings for Spring, Wicket, etc) that I want to reuse in my integration tests, and I've put them under a 'test' directory (instead of 'main'), but that means that they don't get built and included in the main artifact. Here's how I forced Maven to build it into a test artifact (in the build/plugins section):


... and here's how I included it as a dependency in the other projects:


Base Test Class for Spring

When using Spring to configure my web application, I usually want to employ the same XML configuration file as the application, especially for database access. So here is what I currently use as the base class for all my integration test cases.

public class ServicesTestBase extends AbstractTransactionalDataSourceSpringContextTests {

private static final String CONFIG_LOCATION = "/context/applicationContext.xml";

protected ConfigurableApplicationContext createApplicationContext(String[] locations) {
ConfigurableWebApplicationContext appContext = new XmlWebApplicationContext();

// this is org.springframework.mock.web.MockServletContext
MockServletContext servletContext = new MockServletContext();
servletContext.addInitParameter(JMXSpringMBeanNamingStrategy.CONFIG_INIT_PARAM_NAME, CONFIG_LOCATION);

return appContext;

protected String[] getConfigLocations() {
return new String[]{ CONFIG_LOCATION };

In addition to those two methods, earlier projects have overridden the 'onSetupBeforeTransaction' method as follows, although I found I haven't had to do this for my plain old integration tests:

protected void onSetUpBeforeTransaction() throws Exception {
// this is our own home-grown class which you may not need; source may be available by request
StaticApplicationContext.setContext(this.applicationContext); // Do this BEFORE onSetUpBeforeTransaction()

However, on my Wicket project, I did have to play around with it a bit, so here is what I created for the 'HomePageTest':

protected void onSetUpBeforeTransaction() throws Exception {

StaticApplicationContext.setContext(this.applicationContext); // Do this BEFORE onSetUpBeforeTransaction()


AuthenticatedWebApplication authenticatedWebApp = new BackOfficeApplication() {
public void init() {
addComponentInstantiationListener(new SpringComponentInjector(this, HomePageTest.this.applicationContext));
tester = new WicketTester(authenticatedWebApp);

One thing you might have noticed is that JMXSpringMBeanNamingStrategy, so I'll explain real quick: it's to allow me to share the same applicationContext.xml file with all my web app contexts such that the JMX engine won't break due to duplicate names. It's inserted this way:

<!-- (Initially copied from ) -->
<!-- This bean must not be lazily initialized if the exporting is to happen. (comment from docs) -->
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
<property name="namingStrategy"><bean class="com.max.utils.JMXSpringMBeanNamingStrategy" /></property>
<property name="beans">
<map merge="true">
<entry key="" value-ref="dataSource" />
<entry key="" value-ref="log4jBean" />
<entry key="" value-ref="dynamicTestSettings" />

Here is how it's defined; you can tell that it's not ideal, but it's what I got:

public class JMXSpringMBeanNamingStrategy implements ObjectNamingStrategy, ServletContextAware {

public static final String CONFIG_INIT_PARAM_NAME = "contextConfigLocation";

private ServletContext context;

public void setServletContext(ServletContext _context) {
this.context = _context;

public ObjectName getObjectName(Object managedBean, String beanKey) throws MalformedObjectNameException {
// There's got to be a better way to get the context name of this app!
String springWebAppConfigFile = context.getInitParameter(CONFIG_INIT_PARAM_NAME);
String configName = springWebAppConfigFile;
configName = configName.replace("classpath", "");
configName = configName.replace("webapp", "");
configName = configName.replace("xml", "");
configName = configName.replace("context", "");
configName = configName.replaceAll("[\\:\\/\\.\\-]", "");
String newBeanKey = beanKey.replaceFirst("forWebApp", configName);
return new ObjectName(newBeanKey);