I want completely automated integration testing for a Maven project. The integration tests require that an external (platform-dependent) program is started before running. Ideally, the external program would be killed after the unit tests are finished, but is not necessary.
Is there a Maven plugin to accomplish this? Other ideas?
You could use the antrun plugin. Inside you would use ant's exec apply task.
Something like this.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.2</version>
<executions>
<execution>
<phase> <!-- a lifecycle phase --> </phase>
<configuration>
<tasks>
<apply os="unix" executable="cmd">
<arg value="/c"/>
<arg value="ant.bat"/>
<arg value="-p"/>
</apply>
<apply os="windows" executable="cmd.exe">
<arg value="/c"/>
<arg value="ant.bat"/>
<arg value="-p"/>
</apply>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
Ant support os specific commands of course through the condition task.
I'm currently working on a more specific plugin that could easily be "degraded" to be a simple external task executer but ... there are quite a few other things to consider.
- How do you know the process has actually started?
- What do you do with the return code?
- How do you make sure the executor plugin runs first (bind it to the test-compile phase)?
I'm sure there would be more if I actually started developing the plugin, but is there really a need for a generic executer?
UPDATE:
I guess there is ... there's an excellent set of Maven plugins at CodeHaus. Here's the one you want: http://mojo.codehaus.org/exec-maven-plugin/.
The cargo maven plugin is a good way to go if you're doing servlet development and want to deploy the resulting WAR for integration testing.
When I do this myself, I often set up a multi-module project (although that's not strictly nessecarily) and encapsulate all the integration testing into that one module. I then enable the module with profiles (or not) so that it's not blocking the immediate "yeah, I know I broke it" builds.
Here's the pom from that functional test module - make of it what you will:
<?xml version="1.0"?><project>
<parent>
<artifactId>maven-example</artifactId>
<groupId>com.jheck</groupId>
<version>1.5.0.4-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.jheck.example</groupId>
<artifactId>functional-test</artifactId>
<name>Example Functional Test</name>
<packaging>pom</packaging>
<dependencies>
<dependency>
<groupId>com.jheck.example</groupId>
<artifactId>example-war</artifactId>
<type>war</type>
<scope>provided</scope>
<version>LATEST</version>
</dependency>
<dependency>
<groupId>httpunit</groupId>
<artifactId>httpunit</artifactId>
<version>1.6.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<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>
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
<version>0.3</version>
<configuration>
<wait>false</wait> <!-- don't pause on launching tomcat... -->
<container>
<containerId>tomcat5x</containerId>
<log>${project.build.directory}/cargo.log</log>
<zipUrlInstaller>
<!--
<url>http://www.apache.org/dist/tomcat/tomcat-5/v5.0.30/bin/jakarta-tomcat-5.0.30.zip</url>
-->
<!-- better be using Java 1.5... -->
<url>http://www.apache.org/dist/tomcat/tomcat-5/v5.5.26/bin/apache-tomcat-5.5.26.zip</url>
<installDir>${installDir}</installDir>
</zipUrlInstaller>
</container>
<configuration>
<!-- where the running instance will be deployed for testing -->
<home>${project.build.directory}/tomcat5x/container</home>
</configuration>
</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>com.jheck.example</groupId>
<artifactId>example-war</artifactId>
<type>war</type>
<!-- <properties>
<plan>${basedir}/src/deployment/geronima.plan.xml</plan>
</properties> -->
<pingURL>http://localhost:8080/example-war</pingURL>
</deployable>
</deployables>
</deployer>
</configuration>
</execution>
<execution>
<id>stop-container</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
You probably want to bind your actual integration tests to the integration-test phase of the maven lifecycle. If you use a plugin that fails safe (like the aptly named failsafe plugin) to do the actual testing, you can then run your phases like this:
pre-integration-test: start external application (using the exec plugin or one of the other suggestions here)
integration-test: Run the actual integration tests using the failsafe plugin
post-integration-test: Shut down the external application and do any other necessary cleanup
verify: Have the failsafe plugin verify the results of the test and fail the build at this point
It's fairly straightforward to use the exec plugin, the trick is to get your application started up in the background. You should be careful to make sure that the app is fully up before starting the tests in the next phase. Unfortunately, getting your application up and making sure it's up enough while running in the background is not always a trivial task, and the specifics of how to do that depend on your application. It often involves custom code in the application.
Do you want to start an application server ? Have a look at Cargo and its Maven plugin.