Integration Testing Web Applications

Over the years I have needed to integration test a number of web applications from simple user facing applications to large Web service APIs.  No matter what the application does, one always needs to deploy the application into a container to perform integration testing of the user interface or Web service APIs.  A good development team with an eye on efficiency gets bonus points if this process is handled automatically by the build process.  I’ve been using a combination of Maven, Cargo, and a continuous integration server to cheaply solve this problem for years while incurring minimal overhead during the build process. In this post, I lay out a simple configuration for integrating Cargo into the Maven build process for automated testing and for rapid build, test, fix cycles when tooling like JRebel isn’t available.

While setting up the Cargo Maven 2 Plug-in is not difficult, experience has taught me that a handful of strategic decisions in how the plug-in is configured can dramatically increase its usability and developer productivity.  This post demonstrates the helpful tips that I have uncovered while working with Cargo.  While the examples in this post use Tomcat for the container, the information here is applicable to the other container types that Cargo supports.

You can find an example project leveraging the tips and configuration from this post on GitHub at https://github.com/DavidValeri/blog-cargo-tomcat-example.

Tip One – Parameterize Frequently Changed Configuration Options

When running Cargo, I frequently find that I need to change the container port to avoid port collisions, configure the debug port or configure the debugger to suspend the JVM, or change the artifact that the build deploys to the container.  While you can configure some of these options using “-D” on the command line when you execute the build, I find that not everything that you frequently configure in Cargo is easily configurable from the command line or easy to remember.  As such, I’ve parameterized things like the container port, the debugger port, the suspend option when debugging, and the download URL of the container installer.

The XML fragment below enumerates the properties that I recommend parameterizing in some way when working with Cargo on a regular basis across multiple projects.

    <!-- The Cargo container type -->
    <cargo.container.id>tomcat6x</cargo.container.id>
    <!-- The URL of the Zip file based installer for the container that Cargo is to deploy -->
    <cargo.container.url>https://archive.apache.org/dist/tomcat/tomcat-6/v6.0.32/bin/apache-tomcat-6.0.32.zip</cargo.container.url>
    <!-- The location where downloaded container Zip file installers are cached -->
    <cargo.container.download.dir>${user.home}/.m2/cargo/containers</cargo.container.download.dir>
    <!-- The port used by the forked container -->
    <cargo.container.port>8080</cargo.container.port>
    <!-- When using Tomcat, the AJP connector listen port -->
    <cargo.container.tomcat.ajp.port>8009</cargo.container.tomcat.ajp.port>
    <!-- The RMI port used on the container.  For Tomcat, this is the value of the /Server/@port attribute. -->
    <cargo.container.rmi.port>8205</cargo.container.rmi.port>
    <!-- Optional JVM arguments for the forked container that are prepended to the option
         that configures the Zommons config location and appended to any debugging 
         configuration set by cargo.container.jvmargs.debug (See cargo-debug profile.)-->
    <cargo.container.jvmargs></cargo.container.jvmargs>
    <!-- The port to use when the debugger is enabled. -->
    <cargo.container.debug.port>8000</cargo.container.debug.port>
    <!-- The suspend setting to use when the debugger is enabled. -->
    <cargo.container.debug.suspend>n</cargo.container.debug.suspend>
    <!-- The debug arguments used to enable debugging on the forked JVM. -->
    <cargo.container.debug.jvmargs>-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=${cargo.container.debug.suspend},address=${cargo.container.debug.port} -Xnoagent -Djava.compiler=NONE</cargo.container.debug.jvmargs>
    
    <cargo.deployable.default.groupId>${project.groupId}</cargo.deployable.default.groupId>
    <cargo.deployable.default.artifactId>${project.artifactId}</cargo.deployable.default.artifactId>
    <cargo.deployable.default.context>${project.artifactId}</cargo.deployable.default.context>

Tip Two – Make It Easy To Enable Debugging in the Container

I often find that I need to debug the application running in the container during integration testing.  I can never remember the JVM arguments to enable debugging.  The second tip for increasing productivity with Cargo involves using a Maven profile to enable debug support in the forked container JVM.  By using a Maven profile activated by a property, developers can use an easy to remember property to enable debug support when launching the build.  For example, the following command to build, package, and deploy an application

mvn package cargo:run -Dcargo.debug

is a lot easier to remember than adding the following to a JVM startup command

-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -Xnoagent -Djava.compiler=NONE

Tip Three – Put the Cargo Plug-In Configuration in Your Parent POM

Put the Cargo plug-in configuration in your parent POM under the <pluginsmanagement> element as shown below.

...
    <pluginManagement>
      <plugins>
        <plugin>
          <groupId>org.codehaus.cargo</groupId>
          <artifactId>cargo-maven2-plugin</artifactId>
          <version>1.3.3</version>
          ...
        </plugin>
      </plugins>
    </pluginManagement>
...

In any WAR packaged module that you wish to enable Cargo for, add the plug-in to the build and the configuration from the parent POM now applies to the module’s build.  Use the parameterized options to override or customize any aspects of the configuration as needed in the module by overriding the properties from the parent POM as shown below.

...
  <properties>
    <!-- Change the forked container port to avoid conflict. -->
    <cargo.container.port>7080</cargo.container.port>
  </properties>
...
    <plugins>
      <!-- Trigger Cargo to start and stop using the configuration inherited from the 
           parent POM's pluginmanagement definition. -->
      <plugin>
        <groupId>org.codehaus.cargo</groupId>
        <artifactId>cargo-maven2-plugin</artifactId>
      </plugin>
    </plugins>
...

Tip Four – Package the Application and Deploy It from the Command Line While Developing

By leveraging Cargo’s ability to deploy your application in a scripted manner from the command line, developers can focus on developing rather than figuring out how to configure a container and deploy the application.  The ability to checkout source code from version control, set a few configuration properties specific to your development environment, and run a simple command to deploy the application to a clean and correctly configured container can increase new developer productivity and decrease the time it takes to perform a build, test, fix cycle with a Web application.  As an added bonus, performing the packaging of the application on the command line through Maven ensures that you are developing against the application as built and deployed by your team’s canonical build system, Maven, and not a developer’s IDE.  If properly configured, you can deploy the application to a container using the following command.

mvn package cargo:run

Once the application deploys, developers can launch integration tests from the command line, their IDE, or any other testing framework that your team uses.

Tip Five – Use the Maven Failsafe Plug-in to Execute Integration Tests Against the Deployed Application

The Maven Failsafe Plug-in works like the Surefire Plug-in used to execute tests during the “test” phase of your Maven build; however, the Failsafe Plug-in executes tests during the “integration-test” phase of the build and does not fail the build on test failures until after the “post-integration-test” phase of the build.  These two distinctions make this plug-in uniquely suited for executing tests against your Web application when deployed by Cargo during the build by executing integration tests at the right time and also allowing the Maven build to clean up after the integration tests should their be test failures.  The app module of the example code contains a sample JUnit based test that  executes as an integration test during the build.

About these ads
This entry was posted in Uncategorized and tagged , , , , . Bookmark the permalink.

One Response to Integration Testing Web Applications

  1. Pingback: Tracking Integration Test Coverage with Maven and SonarQube | David Valeri's Blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s