Monday, June 22, 2009

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";

@Override
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);
appContext.setServletContext(servletContext);

appContext.setConfigLocations(locations);
appContext.refresh();
return appContext;
}

@Override
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:


@Override
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()
super.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':


@Override
protected void onSetUpBeforeTransaction() throws Exception {

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

super.onSetUpBeforeTransaction();

AuthenticatedWebApplication authenticatedWebApp = new BackOfficeApplication() {
@Override
public void init() {
addComponentInstantiationListener(new SpringComponentInjector(this, HomePageTest.this.applicationContext));
initForAppAndAllTests();
}
};
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 http://static.springframework.org/spring/docs/2.0.x/reference/jmx.html ) -->
<!-- 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="com.max.services.forWebApp:name=data-source" value-ref="dataSource" />
<entry key="com.max.services.forWebApp:name=log4j-settings" value-ref="log4jBean" />
<entry key="com.max.services.forWebApp:name=test-settings" value-ref="dynamicTestSettings" />
</map>
</property>
</bean>


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);
}

}

No comments: