9

I have a Maven-based web app project that generates a WAR to be run in Tomcat. Let's suppose, for the sake of argument, that it's vitally important for that project's unit tests to actually send/receive requests over the network (rather than simply calling servlet methods with a mock request).

Is there any way for my test harness to run an instance of Tomcat in the same JVM, load the current project, and let the test cases hit it on localhost? Failing that, how would I actually programatically package the current project (along with dependencies) into a WAR so that I could use Cargo to upload it programatically to some other Tomcat instance? Is there any better alternative than just shelling out to mvn?

I know my request is unusual and unit tests should be more self-contained, etc, but please let's just play along :)

Adrian Petrescu
  • 16,629
  • 6
  • 56
  • 82
  • 3
    Why can't you look at embedded tomcat http://tomcat.apache.org/tomcat-7.0-doc/api/org/apache/catalina/startup/Embedded.html – Arun P Johny Nov 16 '12 at 03:33
  • possible duplicate of [Howto embed Tomcat 6?](http://stackoverflow.com/questions/640022/howto-embed-tomcat-6) – Chris Gerken Nov 16 '12 at 03:40
  • 1
    @ArunPJohny: That was EXACTLY what I was looking for -- it worked great! If you (or anyone who wants to) put it as an answer, I'll accept it :) – Adrian Petrescu Nov 16 '12 at 20:07

4 Answers4

7

You can use an embedded Tomcat for exactly this purpose. Simply set up a static instance in your test harness, and shut it down at the end. Here's some sample code:

import org.apache.catalina.LifecycleException;
import org.apache.catalina.startup.Tomcat;
import org.junit.AfterClass;
import org.junit.BeforeClass;

public class TomcatIntegrationTest {
  private static Tomcat t;
  private static final int TOMCAT_PORT = 9999;

  @BeforeClass
  public static void setUp() throws LifecycleException {
    t = new Tomcat();
    t.setBaseDir(".");
    t.setPort(TOMCAT_PORT);
    /* There needs to be a symlink to the current dir named 'webapps' */
    t.addWebapp("/service", "src/main/webapp"); 
    t.init();
    t.start();
  }

  @AfterClass
  public static void shutDownTomcat() throws LifecycleException {
    t.stop();
  }
}
Adrian Petrescu
  • 16,629
  • 6
  • 56
  • 82
Arun P Johny
  • 384,651
  • 66
  • 527
  • 531
6

Jetty works really well for this purpose. If you're not locked into having to have Tomcat, you could use this quite easily during the integration test phase. Have pre-integration start jetty, and post-integration stop it and you can throw request at your war file because it's running in an actual container.

Michael
  • 6,141
  • 2
  • 20
  • 21
  • Downvote because the question is about Tomcat. The approach you describe is possible with Tomcat, so no need to switch container. – Martin Ellis Nov 16 '12 at 11:05
  • Just providing an alternative approach, and you'll notice that I specifically mentioned "not locked into having to have Tomcat". Yes, you can do Tomcat embedded as well, but I've found Jetty to be much lighter in this usage. – Michael Nov 17 '12 at 03:40
2

I would suggest to create appropriate integration tests and use the following setup to do integration tests during maven build.

You can use the following part to download tomcat or if you need use an artifact from your repository.

<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
<configuration>
    <wait>false</wait>
    <container>
        <containerId>tomcat${tomcat.major}x</containerId>
        <zipUrlInstaller>
            <url>http://archive.apache.org/dist/tomcat/tomcat-${tomcat.major}/v${tomcat.version}/bin/apache-tomcat-${tomcat.version}.tar.gz</url>
            <extractDir>${project.build.directory}/extract/</extractDir>
            <downloadDir>${project.build.directory}/download/</downloadDir>
        </zipUrlInstaller>
        <output>${project.build.directory}/tomcat${tomcat.major}x.log</output>
        <log>${project.build.directory}/cargo.log</log>
    </container>
    <configuration>
        <home>${project.build.directory}/tomcat-${tomcat.version}/container</home>
        <properties>
            <cargo.logging>high</cargo.logging>
            <cargo.servlet.port>9080</cargo.servlet.port>
            <cargo.tomcat.ajp.port>9008</cargo.tomcat.ajp.port>
        </properties>
    </configuration>
</configuration>

The next part is used start and deploy your application into the given tomcat (also working with jetty).

  <executions>
    <execution>
        <id>start-container</id>
        <phase>pre-integration-test</phase>
        <goals>
            <goal>start</goal>
            <goal>deploy</goal>
        </goals>
        <configuration>
            <deployer>
                <deployables>
                    <deployable>
                        <groupId>${project.groupId}</groupId>
                        <artifactId>mod-war</artifactId>
                        <type>war</type>
                        <pingURL>http://localhost:9080/mod-war</pingURL>
                        <pingTimeout>30000</pingTimeout>
                        <properties>
                            <context>mod-war</context>
                        </properties>
                    </deployable>
                </deployables>
            </deployer>
        </configuration>
    </execution>

and of course finally to stop the started server via:

<execution>
    <id>stop-container</id>
    <phase>post-integration-test</phase>
    <goals>
        <goal>stop</goal>
    </goals>
</execution>

The best thing is to put this kind of configuration etc. into a separate maven module which might be called app-it (it for integration test). The complete cycle of testing can simply be called by

mvn verify

whereas in verify lifecycle the integration test phase will be running and which starts the above configuration. But it's important to configurate the maven-failsafe-plugin to get the integration tests itself be running. Detailed description about this can be found in Maven Unit- and Integration Test Guide

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-failsafe-plugin</artifactId>
  <version>2.12</version>
  <executions>
    <execution>
      <id>integration-test</id>
      <goals>
        <goal>integration-test</goal>
      </goals>
    </execution>
    <execution>
      <id>verify</id>
      <goals>
        <goal>verify</goal>
      </goals>
    </execution>
  </executions>
</plugin>
khmarbaise
  • 92,914
  • 28
  • 189
  • 235
0

What you can do is to generate an archetype with tomcat maven plugin from Apache see http://tomcat.apache.org/maven-plugin-2.0/archetype.html.

You will have various samples:

  • embeded junit tests
  • start a tomcat instance with a war and run selenium tests

HTH

Olivier Lamy
  • 2,280
  • 1
  • 14
  • 12