Testing J2EE Application

In this last section you’ll learn how to automate functional testing of the EAR built previously. At the time of writing, Maven only supports integration and functional testing by creating a separate module. To achieve this, create a functional-tests module as shown below:

Figure 4-13: The new functional-tests module amongst the other DayTrader modules

This module has been added to the list of modules in the daytrader/pom.xml so that it’s built along with the others. Functional tests can take a long time to execute, so you can define a profile to build the functional-tests module only on demand. For example, modify the daytrader/pom.xml file as follows:

<modules>
<module>ejb</module>
<module>web</module>
<module>streamer</module>
<module>wsappclient</module>
<module>ear</module>
</modules>
<profiles>
<profile>
<id>functional-test</id>
<activation>
<property>
<name>ci</name>
<value>true</value>
</property>
</activation>
<modules>
<module>functional-tests</module>
</modules>
</profile>
</profiles>

Note: For more information on the ci configuration, see Chapter 7.

This means that running mvn install will not build the functional-tests module, but running mvn install -Pfunctional-test will.

Now, take a look in the functional-tests module itself. The figure below shows how it is organized:

  • Functional tests are put in src/it/java
  • Classpath resources required for the tests are put in src/it/resources (this particular example doesn’t have any resources).
  • The Geronimo deployment Plan file is located in src/deployment/geronimo/plan.xml.

Figure 4-14: Directory structure for the functional-tests module

As this module does not generate an artifact, the packaging should be defined as pom. However, the compiler and Surefire plugins are not triggered during the build life cycle of projects with a pom packaging, so these need to be configured in the functional-tests/pom.xml file:

<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.geronimo.samples.daytrader</groupId>
<artifactId>daytrader</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>daytrader-tests</artifactId>
<name>DayTrader :: Functional Tests</name>
<packaging>pom</packaging>
<description>DayTrader Functional Tests</description>
<dependencies>
<dependency>
<groupId>org.apache.geronimo.samples.daytrader</groupId>
<artifactId>daytrader-ear</artifactId>
<version>1.0-SNAPSHOT</version>
<type>ear</type>
<scope>provided</scope>
</dependency>
[...]
</dependencies>
<build>
<testSourceDirectory>src/it</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<executions>
<execution>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
</execution>
</executions>
</plugin>
[...]
</plugins>
</build>
</project>

As you can see there is also a dependency on the daytrader-ear module. This is because the EAR artifact is needed to execute the functional tests. It also ensures that thedaytrader-ear module is built before running the functional-tests build when the full DayTrader build is executed from the top-level in daytrader/.

For integration and functional tests, you will usually utilize a real database in a known state. To set up your database you can use the DBUnit Java API (see http://dbunit.sourceforge.net/). However, in the case of the DayTrader application, Derby is the default database configured in the deployment plan, and it is started automatically by Geronimo. In addition, there’s a DayTrader Web page that loads test data into the database, so DBUnit is not needed to perform any database operations.

You may be asking how to start the container and deploy the DayTrader EAR into it. You’re going to use the Cargo plugin to start Geronimo and deploy the EAR into it.

As the Surefire plugin’s test goal has been bound to the integration-test phase above, you’ll bind the Cargo plugin’s start and deploy goals to the preintegration-test phase and the stop goal to the post-integration-test phase, thus ensuring the proper order of execution.

Start by adding the Cargo dependencies to the functional-tests/pom.xml file:

<project>
[...]
<dependencies>
[...]
<dependency>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-core-uberjar</artifactId>
<version>0.8</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-ant</artifactId>
<version>0.8</version>
<scope>test</scope>
</dependency>
</dependencies>

Then create an execution element to bind the Cargo plugin’s start and deploy goals:

<build>
<plugins>
[...]
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
<configuration>
<wait>false</wait>
<container>
<containerId>geronimo1x</containerId>
<zipUrlInstaller>
<url>http://www.apache.org/dist/geronimo/1.0/
geronimo-tomcat-j2ee-1.0.zip</url>
<installDir>${installDir}</installDir>
</zipUrlInstaller>
</container>
</configuration>
<executions>
<execution>
<id>start-container</id>
<phase>pre-integration-test</phase>
<goals>
<goal>start</goal>
<goal>deploy</goal>
</goals>
<configuration>
<deployer>
<deployables>
<deployable>
<groupId>org.apache.geronimo.samples.daytrader</groupId>
<artifactId>daytrader-ear</artifactId>
<type>ear</type>
<properties>
<plan>
${basedir}/src/deployment/geronimo/plan.xml
</plan>
</properties>
<pingURL>http://localhost:8080/daytrader</pingURL>
</deployable>
</deployables>
</deployer>
</configuration>
</execution>
[...]

The deployer element is used to configure the Cargo plugin’s deploy goal. It is configured to deploy the EAR using the Geronimo Plan file. In addition, a pingURL element is specified so that Cargo will ping the specified URL till it responds, thus ensuring that the EAR is ready for servicing when the tests execute.

Last, add an execution element to bind the Cargo plugin’s stop goal to the post-integration-test phase:

[...]
<execution>
<id>stop-container</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

The functional test scaffolding is now ready. The only thing left to do is to add the tests in src/it/java.

An alternative to using Cargo’s Maven plugin is to use the Cargo Java API directly from your tests, by wrapping it in a JUnit TestSetup class to start the container in setUp() and stop it in tearDown().

You’re going to use the HttpUnit testing framework (http://httpunit.sourceforge.net/) to call a Web page from the DayTrader application and check that it’s working. Add the JUnit and HttpUnit dependencies, with both defined using a test scope, as you’re only using them for testing:

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>httpunit</groupId>
<artifactId>httpunit</artifactId>
<version>1.6.1</version>
<scope>test</scope>
</dependency>

Next, add a JUnit test class called src/it/java/org/apache/geronimo/samples/daytrader/FunctionalTest.java. In the class, the http://localhost:8080/daytrader URL is called to verify that the returned page has a title of “DayTrader”:

package org.apache.geronimo.samples.daytrader;

import junit.framework.*;
import com.meterware.httpunit.*;

public class FunctionalTest extends TestCase
{
public void testDisplayMainPage() throws Exception
{
WebConversation wc = new WebConversation();
WebRequest request = new GetMethodWebRequest(
"http://localhost:8080/daytrader");
WebResponse response = wc.getResponse(request);
assertEquals("DayTrader", response.getTitle());
}
}

It’s time to reap the benefits from your build. Change directory into functional-tests, type mvn install and relax:

C:\dev\m2book\code\j2ee\daytrader\functional-tests>mvn install
[...]
[INFO] [cargo:start {execution: start-container}]
[INFO] [cargo:deploy {execution: start-container}]
[INFO] [surefire:test {execution: default}]
[INFO] Setting reports dir: C:\dev\m2book\code\j2ee\daytrader\functional-tests\target/surefire-reports

-------------------------------------------------------
T E S T S
-------------------------------------------------------
[surefire] Running org.apache.geronimo.samples.daytrader.FunctionalTest
[surefire] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0,531 sec
[INFO] [cargo:stop {execution: stop-container}]