Friday, June 5, 2009

Automate Selenium HTML Tests with Ant

I am a huge fan and new convert to Selenium (http://seleniumhq.org). If you are not sure what it is then click on the link and watch the introductory video. If you don't get it after watching the video then don't bother reading on. Just keep testing your apps manually and repeatedly.

The Selenium IDE allows you to record actions in Firefox. These are saved as HTML tests. I have read other articles about automating the tests by first converting them to other formats such as Java. However I love the ease of maintaining test suites via Selenium IDE. If I want to modify a test case or add something to it - this is very easy. If I want to see where or how a particular test case is failing - this is very easy. However if I want to automate these tests by converting them to Java the process of conversion means an extra step for me. And this would be repeated each time I want to modify a test case. I understand you can make the test cases richer if you convert to Java and do things you cannot do with HTML. But I have not run into a case for this yet. Maybe I am understanding it wrong - please correct me if so.

So in order to make life a bit easier I automated the HTML test cases using Selenium RC and Ant. And because I am using Ant this had the added benefit of being able to initialize my database before the tests are run using a DBUnit task (http://www.dbunit.org/).

Firstly I downloaded Selenium RC and extracted selenium-server.jar.

Secondly I modified my build.xml. Make sure the location points to where you saved the selenium-server.jar.










Thirdly I added a few extra tasks to my build.xml. The selenium_run_suite task will start the selenium server. All you need to do once finished is shut it down. An added benefit is that you can specify a location for results of the test suite.

A gotcha that I ran into was that for testing IE it seems you need to use browser="*iehta" and for Firefox browser="*chrome".










suite="selenium\MyFunctionalTestSuite.html"
browser="*chrome"
results="${reports.dir}/selenium/results.html"
multiWindow="true"
timeoutInSeconds="900"
startURL="http://localhost:8456/" />



dest="result.txt" ignoreerrors="true" />






Thanks to Selenium-RC and Continuous Integration on OpenQA where I knicked the selenium_stop_server task.

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!