Beim Testen mit HttpUnit und Spring müssen die Servlets und Filter, die über Spring konfiguriert werden mit ihrem Context versorgt werden. Nimmt man hierzu den “normalen” Context-Listener von Spring, hat das den Nachteil, dass bei jedem Start des HttpUnit-ServletContainers der Context neugeladen wird, was unter Umständen viel Zeit in Anspruch nimmt.
Context cachen
Das Zwischenspeichern des Context ist erforderlich und ähnlich wie Spring selbst Basisklassen mit dieser Eigenschaft anbietet, kann auch ein ContextListener geschrieben werden, der den Context speichert und für viele Testruns zu Verfügung stellt.
Ein solcher Listener sieht z.B. so aus:
package com.rinke.solutions.spring.test; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import org.springframework.beans.BeansException; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.web.context.WebApplicationContext; /** * class that creates and caches an instance of a WebApplicationContext that can act as listener * in a httpunit test environment. so the context is reused by all tests which is much faster than * reinstanciate it again and again. * @author Stefan Rinke */ public class CachingApplicationContextServletContextListener implements ServletContextListener { /** * the instance shared by all tests. */ private static WebApplicationContext ctx; /** * An implemetation of WebApplicationContext that allows the expiclit setting of the servlet * context (maybe a mocked context). * @author ster */ public static class MyWebApplicationContext extends ClassPathXmlApplicationContext implements WebApplicationContext { /** * creates the context an loads the context definition from a classpath location. * @param loc the class path relative location of the context definition. * @throws BeansException if initialization fails */ public MyWebApplicationContext(String loc) throws BeansException { super(loc); } /** * stores the servlet context instance as a ThreadLocal, so that different thread can share * the WebApplicationContext and use different servlet contexts. */ private ThreadLocal servletContext = new ThreadLocal(); /** * return my servlet context for this thread. * @see org.springframework.web.context.WebApplicationContext#getServletContext() */ public ServletContext getServletContext() { return (ServletContext) servletContext.get(); } /** * set the servlet context, which is presented to all 'clients' of the WebApplicationContext. * @param servletContext the context to use */ @SuppressWarnings("unchecked") public void setServletContext(ServletContext servletContext) { this.servletContext.set( servletContext ); } } /** * static factory for the singleton WebApplicationContext * @return the new or cached context */ public synchronized static WebApplicationContext getApplicationContext() { if( ctx == null ) { ctx = new MyWebApplicationContext("/testContext.xml"); } return ctx; } /** * listener callback. populates the cached context with the new initializing servlet context. * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent) */ public void contextInitialized(ServletContextEvent evt) { MyWebApplicationContext ctx = (MyWebApplicationContext) CachingApplicationContextServletContextListener.getApplicationContext(); ctx.setServletContext(evt.getServletContext()); evt.getServletContext().setAttribute( WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ctx ); } /** * does nothing * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent) */ public void contextDestroyed(ServletContextEvent arg0) { } }
Ein weiterer Vorteil des eigenen Listeners: man kann die Context-Definition laden wie man will und ist nicht abhängig, von dem simulierten Verhalten des Servlet-Classloaders von HttpUnit.