Embedded Integration Testing of Web Applications Blog

Version 2


    Do you speak test? In that case: hello, web application:

    public class WebIntegrationTest extends net.sourceforge.jwebunit.WebTestCase { public void testIndex() { beginAt("/index.html"); assertTextPresent("Hello World!"); } private org.mortbay.jetty.Server server; protected void setUp() throws Exception { // Port 0 means "assign arbitrarily port number" server = new org.mortbay.jetty.Server(0); server.addHandler( new org.mortbay.jetty.webapp.WebAppContext("src/main/webapp", "/my-context")); server.start(); // getLocalPort returns the port that was actually assigned int actualPort = server.getConnectors()[0].getLocalPort(); getTestContext().setBaseUrl("http://localhost:" + actualPort + "/my-context"); } }

    This code runs with no application server and no separate deployment step, but it uses HTTP and a production web server. If this looks interesting, keep reading, and I'll show you how it works and a few more tricks, including how to use the same method to unit test a web application constructed with Spring-MVC and Hibernate.

    The "Hello World" Embedded Integration Test

    First: You can download the complete project to run the "hello world" application from the Resourcessection. To run the application, install Maven 2 and run mvn test. To use the code in an IDE, run mvn eclipse:eclipse or mvn idea:idea. (Or see instructions for Netbeans.) After you import the project into your workspace, the test should run in memory, just as a normal unit test.

    If you want to follow the example by yourself, you can start out by installing Maven 2, and running the Maven Archetype command like so: mvn archetype:create -DgroupId=no.brodwall.insanejava.demo -DartifactId=web-demo -DarchetypeArtifactId=maven-archetype-webapp. This will create a skeletal Maven project.

    Open the pom.xml file and add two more test dependencies:groupId jwebunit, artifactId jwebunit, version 1.2 and groupId org.mortbay.jetty,artifactId jetty, version 6.1.0. Create a directory named src/test/java and place theWebIntegrationTest shown above in it. For example, I would place mine insrc/test/java/no/brodwall/web/integration/WebIntegrationTest.java.

    If you have done everything right, your test will fail. This is because we haven't added an index.html file. You can simply rename the Maven-generated file src/main/webapp/index.jsp toindex.html. Run the tests again and they should succeed. Congratulations, you have successfully run an embedded web integration test.

    Spring Application Context

    Now, let's try out a more sophisticated example: using Spring-MVC and interacting with the Controller to control the result. To do the rendering, I use FreeMarker, a "template engine," or in other words, "a generic tool to generate text output based on templates." FreeMarker combines textual templates with Java objects to create a view. Unlike JSPs, it doesnot require a separate compile step. This means that it starts up faster, which is good for unit tests. The use of FreeMarker requires two new dependencies in the Maven 2 pom (org.springframework:spring:2.0.1 andfreemarker:freemarker:2.3.8--and remember to regenerate IDE files).

    Okay, enough talk, here's another test forWebIntegrationTest:

    public void testShowObject() { // Set up the objects that the controller depends on ModelObject pretendObject = new ModelObject(); pretendObject.setAttributeOne("value one"); pretendObject.setAttributeTwo(1234); // Set up the controller with the dummy ModelObject MyController controller = (MyController)getWebContextBean("controller"); controller.setResult(pretendObject); // Execute the HTTP request beginAt("/my/controller.html"); // Verify the result assertTextInElement("attributeOne", pretendObject.getAttributeOne()); assertTextInElement("attributeTwo", Long.toString(pretendObject.getAttributeTwo())); }

    ModelObject and MyController are classes in our application, so you should create them.MyController will be the controller in this Model-View-Controller application, which means that it will be the class that handles the actual request. The ModelObjectwill be a dependency it needs to do its job.

    The test verifies that the values placed in the object that we injected into the controller are displayed on the resulting web page. Getting it to pass will require us to set up most of the infrastructure for a Spring-MVC application. The first, and maybe most interesting, question in your mind is probably: How do I get the Spring bean from the Web Context? Luckily, Spring provides a way to do this, but you need to get to the right objects. Here is the implementation. Take a deep breath:

    import org.mortbay.jetty.webapp.WebAppContext; import javax.servlet.ServletContext; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; public class WebIntegrationTest extends WebtestCase { // ... private Object getWebContextBean(String beanName) { WebAppContext jettyWebAppContext = (WebAppContext) server.getHandler(); ServletContext servletContext = jettyWebAppContext.getServletHandler().getServletContext(); WebApplicationContext springWebAppContext = 
    WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext); return springWebAppContext.getBean(beanName); } }

    This test will currently fail. In order for Spring tohave an application context, we need to set up a few more things: the src/main/webapp/web.xml must have aContextLoaderListener, and we need thesrc/main/webapp/WEB-INF/applicationContext.xml. Personally, I prefer adding the ContextLoaderListenerand contextConfigLocation first, and then getting the "FileNotFound" for the applicationContext.xml and adding that. This helps me remember what to do next. Here is theweb.xml snippet:

    <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>

    src/main/webapp/WEB-INF/applicationContext.xml will in this case contain a single bean definition: <bean id="controller" class="no.brodwall.insanejava.webintegration.MyController" />).

    For the actual integration, we need to map the /mypath to Spring-MVC's DispatcherServlet and/my/controller.html to the controller. This is done inweb.xml and the new my-servlet.xml. Add the org.springframework.web.servlet.DispatcherServletto src/main/webapp/WEB-INF/web.xml:

    <servlet> <servlet-name>my</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>my</servlet-name> <url-pattern>/my/*</url-pattern> </servlet-mapping>

    Running the tests again will give the following useful (really!) message: "Could not open ServletContext resource [/WEB-INF/my-servlet.xml]". Create a new file,src/main/webapp/WEB-INF/my-servlet.xml:

    <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="handlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="/controller.html">controller</prop> </props> </property> </bean> </beans>

    After adding this code, running the test now gives me: "No adapter for handler [no.brodwall.web.MyController@80cac9]: Does your handler implement a supported interface like Controller?." Which of course it doesn't. So we makeMyController implementorg.springframework.web.servlet.mvc.Controller, which means implementing the handleRequest:

    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) { return new ModelAndView("showResult", "result", this.result); }

    Here, this.request was the value we set in the test.

    Now I get a "404 not found." This is because I have not told Spring-MVC how to deal with the showResult view. I have to inform Spring-MVC about my decision to use FreeMarker. Here is the Spring configuration of FreeMarker in my-servlet.xml:

    <bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"> <property name="cache" value="true" /> <property name="prefix" value="" /> <property name="suffix" value=".ftl" /> </bean> <bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"> <property name="templateLoaderPath" value="/views" /> </bean>

    I can run the tests again. In that case, I get the sensible message: "Template showResult.ftl not found." Place this file in src/main/views/showResult.ftl:

    <html> <body> <p>Attribute One: <span id="attributeOne">${result.attributeOne}</span></p> <-- my only gripe with FreeMarker: By default, numbers are formatted fancy. string("#") overrides it --> <p>Attribute Two: <span id="attributeTwo">${result.attributeTwo?string("#")}</span></p> </body> </html>

    The test now passes. Here it is again:

    public void testShowObject() { // Set up the objects that the controller depends on ModelObject pretendObject = new ModelObject(); pretendObject.setAttributeOne("value one"); pretendObject.setAttributeTwo(1234); // Set up the controller with the dummy ModelObject MyController controller = (MyController)getWebContextBean("controller"); controller.setResult(pretendObject); // Execute the HTTP request beginAt("/my/controller.html"); // Verify the result assertTextInElement("attributeOne", pretendObject.getAttributeOne()); assertTextInElement("attributeTwo", Long.toString(pretendObject.getAttributeTwo())); }

    What Just Happened?

    In the beginning of the article, we added the JWebUnit HTTP-based test library for web applications to the Maven project descriptor (POM). JWebUnit has allowed us to write simple unit tests that verify the output of the web pages using HTTP. It's very important to notice that despite being reasonably fast (about two seconds on my computer), these tests actually run with a real Java servlet container.

    Figure 1 shows the composition of our Spring-MVC application stack.

    Simple example with Spring-MVC
    Figure 1. The composition of a Spring-MVC application stack

    The tests do the following:

    1. Start the Jetty application server with a web context defined by our web.xml file.
    2. The web.xml file sets up a Spring-MVC controller and maps it to a URL.
    3. The test gets the controller out of the Spring Web Application Context, and set its result object.
    4. The test uses the JWebUnit framework to accesses the controller via HTTP.
    5. The test verifies that the HTML returned over HTTP matched our expectations; that is, the attributes displayed in the web page were those we injected into the controller.

    This example is pretty basic, but it serves as a base for more advanced applications. Later, I will show how to put together a complete CRUD (Create Retrieve Update Delete) application with Hibernate, but first, I want so show how you can test this application interactively.

    Starting Jetty for Interactive Use

    For minimal hassle, I usually create a class with a main method. This is src/test/java/no/brodwall/web/WebServer.java:

    public class WebServer { public static void main(String[] args) throws Exception { Server server = new Server(8080); server.addHandler(new WebAppContext("src/main/webapp", "/web-demo")); server.start(); } }

    Run the main class, and go to http://localhost:8080/web-demo/my/controller.html. You will get a nice stack trace from FreeMarker. This is because the result object returned by the controller isnull. Create a default object insrc/main/webapp/WEB-INF/applicationContext.xml to fix this problem:

    <bean id="controller" class="no.brodwall.web.MyController"> <property name="result"> <bean class="no.brodwall.web.ModelObject"> <property name="attributeOne" value="one" /> <property name="attributeTwo" value="2" /> </bean> </property> </bean>

    You have to restart the application server before you can behold the beauty, but when you do, you should be able to feast your eyes on a very simple web page. Like JSPs, FreeMarker templates will be reloaded at runtime if you edit them. Try and modifysrc/main/webapp/views/showResult.ftl and hit the Refresh button in your browser.

    Alternatively, you can use the Maven 2 Jetty plugin, by adding the <plugin> section to the pom.xml file. I prefer having my own main method; that way, debugging the application is much simpler.

    Integration Testing a Simple CRUD Application

    The introduction showed how to get started testing and implementing a web application. I will now describe how to test a full-fledged application. The application will allow for retrieving and updating of a domain object, demonstrating the R and U of the acronym CRUD (Create Retrieve Update Delete). The C (create) and the D (delete) are left as exercises to the reader.

    The article will not describe how to implement the code to make the tests pass, but only the tests themselves. If you're curious about the specifics, please see the companion source code (in theResources section).

    Index: Show a List of Objects

    The full application will be a CRUD application for a single domain class. I am using a class named Category, as seen in Figure 2, as an example. Each category has a name and a year it was created. Categories also have subcategories, but we will ignore those for now. I will start the testing effort using atest doubles for the Data Access Object (DAO). This lets me focus on the web code first. In order to make the tests pass, you should implement your FakeCategoryDAO as simply as you can while getting the tests to run.

    Category domain model
    Figure 2. The Category class

    Here is a test showing how our web application shows a list of categories. Put this test in a newCategoryWebIntegrationTest class with the same setup of Jetty as our simple test:

    public void testIndex() { 
    // Create a Fake DAO and test data CategoryDAO categoryDao = new FakeCategoryDAO(); Category firstCategory = new Category("first category"); Category secondCategory = new Category("second category"); categoryDao.insert(firstCategory); categoryDao.insert(secondCategory); 
    // Inject the DAO it into the controller CategoryListController controller = (CategoryListController)getWebContextBean("listController"); controller.setDao(categoryDao); 
    // Go the the index page beginAt("category/index.html"); 
    // Verify that all data we expect is present assertTablePresent("categories"); assertTextInTable("categories", firstCategory.getName()); assertTextInTable("categories", secondCategory.getName()); }

    Like my simpler test, I manipulate the model objects behind the controller, in this case the DAO that contains all our categories. The test consists of four steps:

    1. Set up test data: Create the underlying data to be displayed and manipulated.
    2. Set up test objects: Replace the production DAO with the test double. We have to do this for all relevant controllers.
    3. Execute the code to be tested: In our case, this means executing a dialog towards a web page using JWebUnit.
    4. Check the result: In our case, this means checking the values displayed on a particular web page.

    All tests of the CRUD web application follow the same pattern. Again, see the companion source code on how to make the tests pass.


    Let's zoom in on one of these categories:

    public void testDetail() { 
    // Create a Fake DAO and test data CategoryDAO categoryDao= new FakeCategoryDAO(); Category category = new Category("first category", 2007); categoryDao.insert(category); 
    // Inject the DAO it into the controllers CategoryListController controller = (CategoryListController)getWebContextBean("listController"); controller.setDao(categoryDao); CategoryController categoryController = (CategoryController)getWebContextBean("categoryController"); categoryController.setDao(categoryDao); 
    // Go to the list page and navigate to our object beginAt("category/index.html"); clickLinkWithText(category.getName()); 
    // Verify that all attributes are displayed assertTextInElement("name", category.getName()); assertTextInElement("creationYear", Integer.toString(category.getCreationYear())); }

    I added a creationYear property to theCategory class, and a constructor that sets it up. I also created a new controller: CategoryController. Just creating dummies for these will make the code compile. AddcategoryController tosrc/main/webapp/WEB-INF/applicationContext.xml to make it available in the application context.

    The interesting parts of the test are in the second half. We expect the index page to have a link to our Categoryobject. And we expect the target of the link to display all properties of the Category.


    If you follow along and try to write the code to make the tests pass as well, you will have noticed that progress is probably slow at this point. We are still creating the infrastructure, so this is to be expected. Personally, I notice a markable speed-up at this point. Let's try to edit the name of a category:

    public void testEditName() { 
    // Refactor out the setup of the test data from previous tests populateTestdata(); String newName = "new subcategroy 1 name"; 
    // Go to and verify the edit page beginAt("category/edit.html?id=" + category.getId()); assertFormElementEquals("name", category.getName()); 
    // Update and submit the form setFormElement("name", newName); submit(); 
    // Check the update assertTextInElement("name", newName); 
    // To avoid a temporary value from being displayed beginAt("category/show.html?id=" + category.getId()); assertTextInElement("name", newName); }

    Notice that I have refactored the setup of the test data into a new method. I have updated the other tests correspondingly as well.

    We're Not Out of the Woods Yet

    I have shown how you can test drive a simple CRUD web application using JWebUnit and Jetty. But Spring-MVC and the realities of Java web applications still have a few surprises in stock for us. Taking the code all the way to production requires us to persist the categories. I will implement a simple Hibernate DAO for this.

    The CategoryDAOTest

    Our first order of business is to implement theCategoryDAO interface using Hibernate. This interface has been evolving as we have implemented new functional requirements. Parallel to these changes, you should develop a test class for the CategoryDAO. The new requirement is to make this test case work with the HibernateCategoryDAOand to wire the application up to use theHibernateCategoryDAO. This requires a new trick with Jetty: getting the DataSource injected correctly.

    Here is the current CategoryDAO interface we need to implement our functional requirements. While creating the basic web tests, I used a "fake" implementation of this that stores all the objects in memory. This makes me able to create the tests without slowing down to mess around with Hibernate.

    public interface CategoryDAO { void insert(Category category); Collection<Category> listAll(); Category get(long id); }

    I strongly recommend that you develop in-process integration tests for the HibernateCategoryDAO. For more information on this, see my java.net article on the subject, " Unit Testing Hibernate Mapping Configurations." An example of a test method in CategoryDAOTest would be testing that objects are persisted correctly:

    public void testFindAll() { Category fooCategory = new Category("foo"); Category barCategory = new Category("bar"); Category bazCategory = new Category("baz"); categoryDAO.insert(fooCategory); categoryDAO.insert(barCategory); categoryDAO.insert(bazCategory); categoryDAO.flushAndEvict(); Collection<Category> expected = Arrays.asList(fooCategory, barCategory, bazCategory); Collection<Category> actual = categoryDAO.listAll(); // Use sets to make sure that ordering is ignored assertEquals(new HashSet<Category>(expected), new HashSet<Category>(actual)); } 

    Or testing that unsaved changes are not returned:

    public void testUnsavedChanges() { Category category = new Category("foo"); categoryDAO.insert(category); Category updatedCategory = categoryDAO.get(category.getId()); updatedCategory.setName("bar"); Category storedCategory = categoryDAO.get(updatedCategory.getId()); assertEquals("foo", storedCategory.getName()); }

    This last test is interesting because it will fail! This may lead to hard-to-find bugs in our code. My Hibernate testing article (cited above) explains how to deal with this in detail, so I will just explain it briefly here.

    The problem is that the DAO returns the same objectinstance. This is because our fake DAO just stores objects in memory, but we will have the same problem with Hibernate. Like most object-relation mapping frameworks, Hibernate uses the concept of a session cache (also known as a first-level cache). The session cache is made to ensure that we don't have multiple instances of what is meant to be the same object lying around. To avoid hitting this problem in our test, we can introduce the concept of evicting objects from the cache. Here is the updated test:

    public void testUnsavedChanges() { Category category = new Category("foo"); categoryDAO.insert(category); categoryDAO.flushAndEvict(); Category updatedCategory = categoryDAO.get(category.getId()); categoryDAO.flushAndEvict(); updatedCategory.setName("bar"); Category storedCategory = categoryDAO.get(updatedCategory.getId()); assertEquals("foo", storedCategory.getName()); }

    We will have to implement flushAndEvict for theFakeCategoryDAO as well. We can do this in several ways, but the fastest is to just clone all the objects. See the companion source code for details.

    With a decent CategoryDAOTest in place, we can now create the Hibernate version of the test and of the DAO. I recommend using Spring's Hibernate support, specificallyAnnotationSessionFactoryBean andHibernateTemplate, for this. See the companion source code if you're interested in the details.

    HibernateCategoryDAOTest is a subclass ofCategoryDAOTest that sets the CategoryDAOmember variable of the superclass toHibernateCategoryDAO. I have implemented this by letting CategoryDAOTest initializecategoryDAO with a call to the overrideable methodcreateCategoryDao:

    public class HibernateCategoryDAOTest extends CategoryDAOTest { protected CategoryDAO createCategoryDao() { return new HibernateCategoryDAO(); } }

    Running HibernateCategoryDAOTest will show fail until we initialize the HibernateCategoryDAO correctly with the HibernateTemplate. For now, let's do this increateCategoryDao:

    protected CategoryDAO createCategoryDao() { SingleConnectionDataSource dataSource = new SingleConnectionDataSource(); dataSource.setDriverClassName("org.hsqldb.jdbcDriver"); dataSource.setUrl("jdbc:hsqldb:mem:test"); dataSource.setUsername("sa"); dataSource.setSuppressClose(true); Properties hibernateProperties = new Properties(); hibernateProperties.setProperty(Environment.HBM2DDL_AUTO, "create-drop"); AnnotationSessionFactoryBean sessionFactoryBean = new AnnotationSessionFactoryBean(); sessionFactoryBean.setDataSource(dataSource); sessionFactoryBean.setHibernateProperties(hibernateProperties); sessionFactoryBean.setAnnotatedClasses(new Class[] { Category.class }); try { sessionFactoryBean.afterPropertiesSet(); } catch (Exception e) { throw new RuntimeException("SessionFactory misconfiguration", e); } HibernateTemplate hibernateTemplate = new HibernateTemplate((SessionFactory)sessionFactoryBean.getObject()); return new HibernateCategoryDAO(hibernateTemplate); }

    This code is long and annoying. The most important things to notice are the facts that we're using Spring'sSingleConnectionDataSource as a test data source, and specifying the HSqlDb in-memory database as the database. This means that all it keeps the data in memory. As a result, any changes are lost when the VM shuts down. This means we don't need to clean up anything. But we will have to tell Hibernate to "create-drop" the tables in the database, so Hibernate generates them automatically when our application runs.

    There is a common problem with tests that use the database: one of our test methods left garbage in the database that made the other tests fail. There are a few different ways of fixing it, but here, the best is to use the Transaction Rollback Teardown pattern:

    private PlatformTransactionManager transactionManager; private TransactionStatus status; protected void setUp() throws Exception { super.setUp(); transactionManager = new HibernateTransactionManager(sessionFactory); TransactionDefinition txDef = new DefaultTransactionDefinition(); status = transactionManager.getTransaction(txDef); } protected void tearDown() throws Exception { transactionManager.rollback(status); super.tearDown(); }

    Now we have a working Hibernate implementation of theCategoryDAO and a CategoryIntegrationTestthat shows that we can display an object from aCategoryDAO on a web page. What happens if we put them together?


    Now all the pieces work; we just have to put them together. We create a subclass of the CategoryIntegrationTest for this. We have to make sure to changeCategoryWebIntegrationTest to support creating something other than a FakeCategoryDAO. Do this by changing the populateTestdata line CategoryDAO categoryDao = new FakeCategoryDAO(); to CategoryDAO categoryDao = createCategoryDAO();. MakecreateCategoryDAO() protected soHibernateCategoryWebIntegrationTest can override it:

    public class HibernateCategoryWebIntegrationTest extends CategoryIntegrationWebTest { protected CategoryDAO createCategoryDao() { // Copied from HibernateCategoryDAOTest } }

    Using Real(er)DataSources

    Before we continue, it's a good idea to do some refactoring. The creation of our HibernateSessionFactory is pretty complex. Let's put it in a separate class, namedCategorySessionFactoryBean. In bothHibernateCategoryWebIntegrationTest andHibernateCategoryDAOTest I'd likecreateCategoryDao to look like this:

    protected CategoryDAO createCategoryDAO() { DataSource dataSource = CategorySessionFactoryBean.inmemoryDataSource(); SessionFactory sessionFactory = CategorySessionFactoryBean.create(dataSource); return new HibernateCategoryDAO(new HibernateTemplate(sessionFactory)); }

    Put CategorySessionFactoryBean in the main code, so the servlet code can use it as well.

    Second, I restructure the web integration test classes. MakeCategoryWebIntegrationTest abstract and create a newFakeDAOCategoryIntegrationTest subclass that overridesCategoryWebIntegrationTest.createCategoryDAO(). This means that we only inherit from abstract classes. In my experience, this leads to better structure. In this particular case, I want to do this before I change the Spring configuration to return aHibernateCategoryDAO.

    Now we're ready to update the test WebService main class we made to test the application interactively. We want it to use Hibernate as well. In order to do that, let's make sure that the normal application context uses Hibernate. First, we updateHibernateCategoryWebIntegrationTest.createCategoryDAOto use the DAO in the Spring application context:

    protected CategoryDAO createCategoryDAO() { return (CategoryDAO)getWebContextBean("categoryDao"); }

    In order to get this to work, you will have to update how the data source is configured insrc/main/webapp/WEB-INF/applicationContext.xml:

    <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="jdbc/primaryDs" /> <property name="resourceRef" value="true" /> </bean>

    We declare the data source as aJndiObjectFactoryBean. This means that we need to set up the JNDI name jdbc/primaryDs before we start up Jetty.

    Jetty and JNDI

    We want to get the data source from JNDI. This means that we have to configure Jetty to give the web application a JNDI data source. It was surprisingly hard to find the documentation for this, but as it turns out, it is very simple. We have to create a new org.mortbay.jetty.plus.naming.Resource object. The constructor for this object automatically registers itself with JNDI. In order to get the JNDI functionality to work, we must add dependencies to jetty-plus andjetty-naming in our project's Maven 2 pom.xmlfile. All our Jetty dependencies should use the same version. (Jetty developers, if you read this: please make the dependencies from jetty-plus to jetty andjetty-naming compile dependencies, notprovided dependencies, so I wouldn't have to do this!)

    When we've added the new dependencies, we can changeCategoryWebIntegrationTest.setUp to create theResource for the data source before Jetty is started.

    protected void setUp() throws Exception { server = new org.mortbay.jetty.Server(0); WebAppContext webAppContext = new WebAppContext("src/main/webapp", contextName); webAppContext.setConfigurationClasses(new String[] { 
    // These are default, but will be overwritten if we don't specify them "org.mortbay.jetty.webapp.WebInfConfiguration", "org.mortbay.jetty.plus.webapp.Configuration", "org.mortbay.jetty.webapp.JettyWebXmlConfiguration", "org.mortbay.jetty.webapp.TagLibConfiguration", 
    // This enables the <resource-ref> section of web.xml "org.mortbay.jetty.plus.webapp.EnvConfiguration", }); 
    // This is needed to avoid error on subsequent Environment registrations org.mortbay.jetty.plus.naming.NamingEntry.setScope(NamingEntry.SCOPE_GLOBAL); 
    // This actually registers the resource 
    new org.mortbay.jetty.plus.naming.Resource( "jdbc/primaryDs", CategorySessionFactoryBean.inmemoryDataSource()); server.start(); int actualPort = server.getConnectors()[0].getLocalPort(); getTestContext().setBaseUrl("http://localhost:" + actualPort + "/my-context"); }

    Finally, we need to updatesrc/main/webapp/WEB-INF/web.xml and add the<resource-ref> section that lets the container know we expect a DataSource:

    <resource-ref> <description>Primary Data Source</description> <res-ref-name>jdbc/primaryDs</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>application</res-auth> </resource-ref>

    This completes the wiring together of the application.

    To the Server!

    Of course, in production, we probably want to use a "real" database and a production server. This means exchanging thejdbc/primaryDs resource with a connection to MySQL, Oracle, or whatever database you use. I have createdMySqlCategoryWebIntegrationTest andMySqlCategoryDAOTest, which uses MySQL to test. See the companion source code for details. I also modified theWebServer class to use MySQL.

    The trickiest difference between test and production scenarios is how to set Hibernate's HBM2DDL_AUTO property. I have implemented this in CategorySessionFactoryBean in the companion source code.

    What about the application server?

    Personally, I would strongly recommend deploying on Jetty in production. Jetty is a production-ready server that runs critical business. Despite the fact that it receives much less attention than Tomcat and JBoss, Jetty runs on about a third as many publicly available sites as Tomcat (Apache-Coyote in the Netcraft study).

    There are two ways in which Jetty is commonly used: either you can use Jetty as a standalone container, like Tomcat or any other, or you could build your own Main-Class that starts up Jetty.

    To deploy on a standalone Jetty instance, we can download Jettyand unpack it. Jetty lets us drop-deploy WAR files, but we have to set up the DataSource in a jetty-env.xml file. This should be located insrc/main/webapp/WEB-INF/jetty-env.xml in our project. See the companion source code for an example of this file. See Jetty's documentation for details on jetty-env.xml. The Jetty documentation also explains how to add DataSourcesfor various databases.

    Use the command mvn package to build the WAR file into the target directory of your project. Copy this file to JETTY_HOME/webapps-plus. Start Jetty by running java -jar start.jar etc/jetty.xml etc/jetty-plus.xml from JETTY_HOME. The web application should now be available.

    The simplest way of deploying on Tomcat is to add a "context" file in Tomcat's scanning directory ($CATALINA_HOME/conf/[engine_name]/[host_name]). If you use the location of your WAR file in the Maven repository as thedocBase for Tomcat, Tomcat will pick up any changes when you execute mvn install. See the example context file src/main/tomcat/web-demo.xml in the companion source code for an example.

    Conclusion, and Unfinished Business

    In this article, I have showed how Jetty can help us bring testing all the way from early development into testing your real production environment. Running Jetty in-process in an embedded integration test lets us control the data and context of our test and lets us test logic related to views, as well as problems that only show up when we put our application in a real container. Figure 3 shows how Jetty runs inside the same VM as the tests and is controlled from the unit test code.

    Embedded testing with Jetty
    Figure 3. How the tests set up the elements of the application

    This article has also given an introduction to building web applications with Spring and Hibernate. However, there are aspects of Spring-MVC and Hibernate that we should have covered. Specifically, when we display more complex object graphs, we will have to deal with having lazily loaded objects resolved while we're rendering the view. This is handled through Spring's OpenSessionInViewFilter. Introducing OpenSessionInViewFilter will also require us to wise up about transactions on the DAO. These problems are left as an exercise for the reader. Leave me a comment if you would like a hint.

    I hope this article gave you some insight into how to effectively test web application. Please, leave a comment if something went wrong along the way, if you have discovered a better way of doing the same thing, or if you just enjoyed the article.