Monday, May 25, 2009

Spring 2.5 - Unit testing with JNDI

My problem was that I had a java.util.Properties object with a bunch of properties in JNDI but I also needed to be able to unit test the classes that were using these properties. I was using Spring so that made things easier.

Being a big fan of Unit Testing I was determined not to let a little thing like JNDI stop me from getting all green on my JUnit test results.

My first step was to separate out some of my applicationContext.xml logic into 2 files. One for the "real" application that will run under GlassFish - let's call it my-jndi.xml. And the other for running unit tests - let's call it my-jndi-test.xml.

my-jndi.xml had the following:







Then in my web.xml I had the following:



contextConfigLocation

/WEB-INF/my-jndi.xml
/WEB-INF/my-application.xml




my-jndi-test.xml had the following:



class="org.springframework.beans.factory.config.PropertiesFactoryBean">


classpath:test.properties


true



Then in my base test class I had the following:


protected String[] getConfigLocations() {
return new String[] {
"file:web/WEB-INF/nexus-jndi-test.xml",
"file:web/WEB-INF/nexus-application.xml"
};

Monday, May 11, 2009

StrutsTestCase, Spring 2.5 and Hibernate

Okay Struts 1 might be "old" technology but we all can´t be working with the latest frameworks. And in it´s defense I actually like the simplicity of Struts 1 - it just works.

Recently I was faced with what I thought would be quite a common scenario - JUnit testing Struts actions in an application that also utilises Spring (for transaction management) and Hibernate with an Open Session In View filter. This presented two problems. The first was how to replicate the Open Session In View filter which I was using for Hibernate. And secondly how to replicate the listener which initialises all the Spring beans. I ended up with a BaseMockStrutsTestCase class which I used in place of MockStrutsTestCase. See below for the complete code.

In regards to the first problem I found a few posts on the Spring forums. And also had a look at the source for org.springframework.orm.hibernate3.support.OpenSessionInViewFilter.

In regards to the second problem I googled around and found Enabling TDD by Integrating Spring and StrutsTestCase by Keith McMillan. This in turn also pointed me to Matt Raible´s site and his appfuse specific BaseMockStrutsTestCase class. This gave me what I need to solve the problem of replicating the problem of initialising the Spring beans.

So now the two issues glued together in one class.


import java.io.File;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.mock.web.MockServletContext;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.orm.hibernate3.SessionHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import servletunit.struts.MockStrutsTestCase;

/**
* This class used as base for testing all struts actions.
*
* 1) Inits Spring Context - normally done by listener on app startup.
* 2) Opens session normally done by open session in view filter.
*
*/

public abstract class BaseMockStrutsTestCase extends MockStrutsTestCase {

protected WebApplicationContext ctx = null;
// open session in view
private Session session;
private SessionFactory sessionFactory;

public BaseMockStrutsTestCase() {
super();
}

public BaseMockStrutsTestCase(String name) {
super(name);
}

protected void setUp() throws Exception {
super.setUp();

// initialize Spring Context
setContextDirectory(new File("web"));
MockServletContext sc = new MockServletContext("");
sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM,"WEB-INF/applicationContext.xml");

ServletContextListener contextListener = new ContextLoaderListener();
ServletContextEvent event = new ServletContextEvent(sc);
contextListener.contextInitialized(event);

// magic bridge to make StrutsTestCase aware of Spring's Context
getSession().getServletContext().setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
sc.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE));

ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(
getSession().getServletContext());

// open session (open session in view)
sessionFactory = (SessionFactory) getSpringBean("sessionFactory");
session = SessionFactoryUtils.getSession(sessionFactory, true);
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
}

public void tearDown() throws Exception {
super.tearDown();
// release session (open session in view)
SessionHolder holder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
session = holder.getSession();
TransactionSynchronizationManager.unbindResource(sessionFactory);
SessionFactoryUtils.closeSession(session);

ctx = null;
}

public Object getSpringBean(String beanId) {
return ctx.getBean(beanId);
}




Be very cafefult that you don´t have 2 Spring contexts. This will result in multiple session being opened and a lot of bashing your head against a wall.

There will be 2 Spring contexts if you have the following in your struts-config.xml.







And the following in your web.xml.



org.springframework.web.context.ContextLoaderListener



You should only need to one entry in your web.xml.

Tuesday, May 5, 2009

Grabbing SVN revision number using ANT

I needed to pull the revision number from SVN using ANT. A google search came up with the following pages: Getting Subversion Revision in Ant and Creating a Build Number With Ant and Subversion. Both had nice solutions but I noticed on both when you scrolled down the comments a fellow by the name of Udo Stark had come up with a much easier solution.













Thanks Udo!