How to test an EAR based JSF application using JSFUnit

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.

Step 1

[...]
<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.

Step 2

[...]
<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.

Step 3

[...]
<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.

Directory Layout

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

Conclusion

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:

  1. 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.
  2. 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:

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).

Summary

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.

8 thoughts on “How to test an EAR based JSF application using JSFUnit

  1. Hi, thanks for your post,
    i am failing to deploy to my local jboss7x server, could you please offer some guidance

  2. Wow that was odd. I just wrote an extremely long comment but after
    I clicked submit my comment didn’t appear. Grrrr… well I’m
    not writing all that over again. Anyhow, just wanted to say superb blog!

  3. Great article, so it will be very nice if it was possible to download the sample project 😉
    Thanks for this article

  4. Hello

    Thank you for the very important information. I have followed all steps but i face a couple of problems/questions.
    – The JSFUnit test should be at myapp-it-war right?
    – The pom.xml of myapp-it-ear does not produce the myapp-it-war.ear file… is this correct?
    – How can i reference from the myapp-it-cargo my myapp-it-ear file to deploy it using an embedded container?

    Thank you in advance!!
    Papapetrou Patroklos

    1. – The JSFUnit test should be at myapp-it-war right?

      Yes. The JSFUnit tests are located in myapp-it-war/src/main/java. This ensures they end up in the newly created .war file.

      The test files are also used from the myapp-it-cargo file. This is the reason why we explicitely attach the source artifact in the myapp-it-war project. The myapp-it-cargo uses the maven dependency plugin to extract the tests locally.

      – The pom.xml of myapp-it-ear does not produce the myapp-it-war.ear file… is this correct?

      myapp-it-ear produces an ear that contains the contains the contents of the original ear file plus the newly created myapp-it-war war artifact. Also the application.xml is patched so that the new version of the war artifact is used.

      – How can i reference from the myapp-it-cargo my myapp-it-ear file to deploy it using an embedded container?

      We use Cargo to start and deploy into a JBoss 4.2.3 AS. I do not know the details on how to setup an embedded server. Regarding cargo we use the following snippet to reference the myapp-it-ear artifact from the myapp-it-cargo project:

      <plugin>
        <groupId>org.codehaus.cargo</groupId>
        <artifactId>cargo-maven2-plugin</artifactId>
        [...]
        <configuration>
            [...]
           <deployer>
             <type>installed</type>
             <deployables>
                  <deployable>
                        <groupId>mygroupid</groupId>
                        <artifactId>myapp-it-ear</artifactId>
                        <type>ear</type>
                        <pingUrl>http://localhost:8080/myUrl</pingUrl>
                        <pingTimeout>180000</pingTimeout>
                   </deployable>
            </deployables>
         </deployer>
      </configuration>
        
      

      Note that myapp-it-ear also needs to be listed in the dependencies element of the myapp-it-cargo pom!

      See the Maven Cargo Plugin documentation for more configuration details.

      Does this clarify things a bit?

      regards,
      Alphonse

    2. Dear Alphonse
      Yes your reply helped me a lot.
      The myapp-it-cargo should be jar type?
      Where should I find the test files in my myapp-it-war and where should I find them in myapp-it-cargo?
      I have completed all the configuration , now by container runs but not tests are executed.
      Regards
      Patroklos

      1. The myapp-it-cargo should be jar type?

        In our case we used the packaging type default jar.
        As it is unnecessary that
        myapp-it-cargo produces any maven artifact it should be possible to set it to pom too.

        Where should I find the test files in my myapp-it-war and where should I find them in myapp-it-cargo?

        As mentioned in the article the tests are located in myapp-it-war/src/main/java.

        The myapp-it-cargo project configures the maven-dependency-plugin to unpack the sources from the myapp-it-war project. The goal unpack we use automatically binds to the maven phase process-sources and extracts the sources to the directory target/dependency by default (see the documentation for details).

        The Builder Helper Maven Plugin is then used to configure target/dependency as an additional source directory for tests.

  5. Stan Silvert, project lead of JSFUnit, made some good comments about our solution in the JSFUnit forum (http://community.jboss.org/message/546107).

    Both the JSFUnit Deployer (http://community.jboss.org/wiki/JSFUnitWithASMicrocontainer) and the JSFUnit Console (http://community.jboss.org/wiki/UsingJSFUnitwithServlet30) look very useful and could help to simplify JSFUnit tests. Unfortunately, both options are not available for us as we are still using JBoss 4.x. Obviously I missed to clarify this point in the post.

    Thanks to Stan Silvert for that feedback.

Comments are closed.