How do you test your JSF application? Using JSFUnit allows you to perform integration test of your JSF application. The tests are run inside the container. JSFUnit tests can access the internal state of your application. The JSFUnit documentation describes how JSFUnit can be integrated into a Maven build environment. It leverages the maven-cargo-plugin and the maven-surefire-plugin to deploy the tests to an application server and execute them using JUnit.
However this is only explained for a plain Web archive (.war). It remains unclear how a JSF application can be tested which is packaged within an Enterprise archive (.ear). Continue to read if you are interested to see how we used JSFUnit to test our EAR based JSF application and how we integrated it in our Maven build environment.
What is JSFUnit?
JSFUnit is a test framework for JSF applications. It is designed to allow complete integration testing and unit testing of JSF applications using a simplified API. JSFUnit tests run inside the container, which provides the developer full access to managed beans, the FacesContext, EL Expressions, and the internal JSF component tree. At the same time, you also have access to parsed HTML output of each client request (http://www.jboss.org/jsfunit).
Steps to create a “JSFUnified” Maven EAR
The starting point for this configuration is the JBoss documentation Wiki: http://community.jboss.org/wiki/JSFUnitWithMaven. The article describes how to create a “JSFUnified” Maven WAR. You should be familiar with it as our solution is based on it.
The following maven modules are part of the application:
- myapp-war: This WAR (Web archive) contains the JSF web application.
- myapp-ear: This EAR (Enterprise archive) contains the myapp-war WAR plus all other EJB JARs needed to run our application.
In addition the application contains other modules which consist of EJB’s and JPA entities. They are not described here as they are not important for the scope of this article.
myapp |-- pom.xml |-- myapp-ear (Maven project to create the main EAR) | `-- pom.xml `-- myapp-war (Maven project to create the main WAR) `-- pom.xml
Create a new submodule for your JSFUnit WAR
The first step is to add a new WAR module to our project. It contains the JSFUnit tests, the JSFUnit library and the dependencies necessary to execute JSFUnit (see the JSFUnit Documentation for the exact set of dependencies). This module uses the maven-war-plugin overlay feature to create a “JSFUnified” WAR.
This is basically described in the JBoss Documentation Wiki mentioned above. In our example the artifact has the name myapp-it-war.
The tests are located in the src/main/java folder to ensure they end up in the WAR. We also configured Maven to create an additional source artifact. This step is necessary as the tests are actually run from a different project and thus need to be made available from there. We will see this later:
<build> <plugins> <plugin> <artifactId>maven-source-plugin</artifactId> <executions> <execution> <phase>package</phase> <goals> <goal>jar-no-fork</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
Create a new submodule for your JSFUnit EAR
The next step is to create a “JSFUnified” EAR. This should be based on the original EAR but should contain the JSFUnified WAR instead of the original one. Unfortunately, the maven-ear-plugin does not contain a similiar overlay functionality as provided by the maven-war-plugin. As workaround, we use he maven-antrun-plugin to execute an embedded Ant script. This could be improved by writing a custom maven plugin that provides the overlay functionality for EAR projects.
We start with a Maven EAR project which has two dependencies: The original EAR and the JSFUnified WAR. The scope of the EAR needs to be set to provided as the maven-ear-plugin does not know how to handle this type of dependencies. We need to provide that functionality manuallly.
Basic Maven project configuration
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>mygroup</groupId> <artifactId>myapp-it-ear</artifactId> <version>0.1-SNAPSHOT</version> <packaging>ear</packaging> <build> [...] </build> <dependencies> <dependencies> <dependency> <groupId>mygroup</groupId> <artifactId>myapp-it-war</artifactId> <type>war</type> <version>0.1-SNAPSHOT</version> </dependency> <dependency> <groupId>mygroup</groupId> <artifactId>myapp-ear</artifactId> <type>ear</type> <scope>provided</scope> <version>0.1-SNAPSHOT</version> </dependency> </dependencies> </project>
We use three Maven plugins to achieve that goal:
The first step is to use the maven-dependency-plugin to unpack the original EAR archive into the current project.
[...] <plugin> <!-- Step 1: this copies and unpacks the original EAR into the target/dependency folder --> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <id>unpack-dependencies</id> <goals> <goal>unpack</goal> </goals> </execution> </executions> <configuration> <artifactItems> <artifactItem> <groupId>mygroup</groupId> <artifactId>myapp-ear</artifactId> <type>ear</type> <version>0.1-SNAPSHOT</version> </artifactItem> </artifactItems> </configuration> </plugin> [...]
The next step is to prepare the application.xml for our patched EAR. We use an embedded Ant snippet to replace the original reference to myapp-war in application.xml with the reference to the JSFUnified version myapp-it-war.
[...] <plugin> <!-- Step 2: patch the original application.xml to use the "JSFUnified" WAR instead of the original one --> <artifactId>maven-antrun-plugin</artifactId> <executions> <execution> <id>patch-application-xml</id> <phase>generate-resources</phase> <goals> <goal>run</goal> </goals> <configuration> <tasks> <replace summary="true" file="target/dependency/META-INF/application.xml" token="myapp-war" value="myapp-it-war"/> </tasks> </configuration> </execution> </executions> </plugin> [...]
The final step is to package the JSFUnified EAR. As we set the packaging type of this project to ear this is automatically done by the maven-ear-plugin. We only need to configure the plugin so that the unpacked and patched contents of the original EAR are included.
[...] <plugin> <!-- Step 3: create the ear based on the unpacked contents from step 1 above --> <artifactId>maven-ear-plugin</artifactId> <configuration> <earSourceDirectory>target/dependency</earSourceDirectory> </configuration> </plugin> </plugins> </build> [...]
Setup it-cargo Project
Finally we need to setup the cargo configuration. We put that configuration into a third maven module named myapp-it-cargo. As a consequence we need to execute tests which belong to another module (myapp-it-war). Using the following Maven configuration, the additional source artifact is unpacked into the myapp-it-cargo module. Thus, tests are compiled and run from it.
<build> <plugins> <plugin> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <id>unpack-dependencies</id> <goals> <goal>unpack</goal> </goals> <configuration> <artifactItems> <artifactItem> <groupId>mygroup</groupId> <artifactId>myapp-it-war</artifactId> <classifier>sources</classifier> <version>0.1-SNAPSHOT</version> </artifactItem> </artifactItems> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>build-helper-maven-plugin</artifactId> <executions> <execution> <id>add-test-source</id> <phase>generate-sources</phase> <goals> <goal>add-test-source</goal> </goals> <configuration> <sources> <source>target/dependency</source> </sources> </configuration> </execution> </executions> </plugin> </plugins> </build>
The Maven Dependency Plugin is used to unpack the sources that come from the myapp-it-war module into the current module. By default the Maven Dependency Plugins extracts this files to the directory target/dependency. Using the Build Helper Maven Plugin this directory is declared as another test source directory. This ensures these tests are also executed during the test phase.
The remaining configuration is similiar to the one described in the JBoss Documentation Wiki (http://community.jboss.org/wiki/JSFUnitWithMaven). We use Cargo to deploy the “JSFUnified” EAR and the maven-surefire-plugin to execute the tests.
This is the resulting directory layout for our application. We used a hierarchical approach to structure the Maven projects.
myapp |-- pom.xml |-- myapp-ear (Maven project to create the main EAR) | `-- pom.xml |-- myapp-war (Maven project to create the main WAR) | `-- pom.xml `-- myapp-it |-- myapp-it-war (Maven project to create the JSFUnified WAR) | `-- pom.xml |-- myapp-it-ear (Maven project to create the JSFUnified EAR) | `-- pom.xml |-- myapp-it-cargo (Maven project to deploy and execute the JSFUnit tests) | `-- pom.xml `-- pom.xml
Our primary goal was to use JSFUnit to test our JSF application which is packaged within an EAR. The good news is that our approach works. The downside is the quite complex project setup. This setup has two effects:
- the initial setup and maintenance of the maven configuration is not simple. However, with a good grasp of the Maven concepts this should not be a problem for the experienced Maven user.
- More critical is the long roundtrip cycle if you are actively developing any tests. After you made a change to any of the tests you need to execute mvn install for at least the three modules myapp-it-war, myapp-it-ear and myapp-it-cargo. This is a rather time consuming process which impedes the developers work.
Based on that we see the following possibilities to further enhance the JSFUnit/Maven/EAR integration:
- re-implement and enhance our current solution using custom Maven plugins.
- investigate if Arquilian (http://community.jboss.org/en/arquillian) can be used to achieve the same goal.
What is Arquilian?
Arquillian enables you to test your business logic in a remote or embedded container. Alternatively, it can deploy an archive to the container so the test can interact as a remote client (http://jboss.org/arquillian).
In this article we showed how to configure Maven to use JSFUnit to test a JSF EAR. We created a “JSFUnified” WAR and EAR and used Cargo to deploy and run the tests.