The
Maven Failsaif plugin makes it very simple to run integration test written with
JUnit or
TestNG. This example shows how to
use the Maven Failsafe Plugin to run Integration Tests that properly
starts and stop a Jetty server during both the pre-integration-test phase and the post-integration-test phase. This solution can be easyly derivated to start and stop any background process that is well integrated as a Maven plugin :
<plugin>
<groupId>${plugin.groupId}</groupId>
<artifactId>${plugin.artifactId}</artifactId>
<version>${plugin.version}</version>
<configuration>
<anything>...</anything>
</configuration>
<executions>
<execution>
<id>do-it-before</id>
<phase>pre-integration-test</phase>
<goals>
<goal>${plugin.start.goal}</goal>
</goals>
<configuration>
<something>...</something>
</configuration>
</execution>
<execution>
<id>do-it-after</id>
<phase>post-integration-test</phase>
<goals>
<goal>${plugin.stop.goal}</goal>
</goals>
<configuration>
<whatever>...</whatever>
</configuration>
</execution>
</executions>
</plugin>
Sometimes, there is no plugin or maven integration that you can use to start and stop the background process(es) required by your intergation test case to run. You can start and stop the desired process(es) manually but let see how to do this properly with Maven.
Maven exec plugin
The
Maven exec plugin provides 2 goals to help execute system and Java programs. However, the programs execution are blocking. It is thus impossible to run tests during the intergation-test phase with running programs started during the pre-integration-test phase. Hopefully, the Maven antrun plugin will help us...
Maven antrun plugin
The
Maven antrun plugin provides the ability to run Ant tasks from within Maven. It can do anything you can put into an ant build.xml script. In particular, it is possible to use the
Ant exec task with all its parameters. The solution to our problem is the
spawn parameter of the exec task that will run the specified executable asynchronously in its own process :
<exec executable="command-to-run"
dir="base/dir/for/the/command"
spawn="true"> <!-- run it asynchronously and in background baby -->
<arg value="arg"/>
<arg value="arg"/>
...
<arg value="arg"/>
</exec>
Okay but now, we want to start third parties programs with the Maven antrun plugin during the
pre-integration-test phase, keeping them running during the
integration-test phase and to shut them down during the
post-integration-test phase. Assuming we have plateform dependent scripts to start and stop Zookeeper,
Kafka or anything else... our pom.xml will look like this :
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<id>start-third-parties</id>
<phase>pre-integration-test</phase>
<configuration>
<target>
<exec executable="${run.command}"
dir="${basedir}/../scripts"
spawn="true">
<arg value="${run.command.additionnal.arg}"/>
<arg value="${basedir}/../scripts/${zookeeper.start.script}"/>
</exec>
<exec executable="${run.command}"
dir="${basedir}/../scripts"
spawn="true">
<arg value="${run.command.additionnal.arg}"/>
<arg value="${basedir}/../scripts/${kafka.start.script}"/>
</exec>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
<execution>
<id>stop-third-parties</id>
<phase>post-integration-test</phase>
<configuration>
<target>
<exec executable="${run.command}"
dir="${basedir}/../scripts"
spawn="false">
<arg value="${run.command.additionnal.arg}"/>
<arg value="${basedir}/../scripts/${zookeeper.stop.script}"/>
</exec>
<exec executable="${run.command}"
dir="${basedir}/../scripts"
spawn="false">
<arg value="${run.command.additionnal.arg}"/>
<arg value="${basedir}/../scripts/${kafka.stop.script}"/>
</exec>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
There is no need to
spawn the shutdown of our third parties programs, but it's possible too...
Okay but what if I want to use both Linux and Windows ?
No problem ! Maven profiles are here to help you. You just have to define two profiles wich
<activation> relies on the os family like this :
<profile>
<id>windows-properties</id>
<activation>
<os>
<family>Windows</family>
</os>
</activation>
<properties>
<run.command>cmd</run.command>
<run.command.additionnal.arg>/c</run.command.additionnal.arg>
<zookeeper.start.script>start-zookeeper.bat</zookeeper.start.script>
<zookeeper.stop.script>stop-zookeeper.bat</zookeeper.stop.script>
<kafka.start.script>start-kafka.bat</kafka.start.script>
<kafka.stop.script>stop-kafka.bat</kafka.stop.script>
</properties>
</profile>
<profile>
<id>linux-properties</id>
<activation>
<os>
<family>unix</family>
</os>
</activation>
<properties>
<run.command>sh</run.command>
<run.command.additionnal.arg></run.command.additionnal.arg>
<zookeeper.start.script>start-zookeeper.sh</zookeeper.start.script>
<zookeeper.stop.script>stop-zookeeper.sh</zookeeper.stop.script>
<kafka.start.script>start-kafka.sh</kafka.start.script>
<kafka.stop.script>stop-kafka.sh</kafka.stop.script>
</properties>
</profile>
Et voila, the start and stop scripts properties will be set correctly depending on the runtime OS.
Okay but I don't want to run whole intergation tests all the time ?
This time again, you can use a Maven profile but without
<activation>. Plugins will be activated only if you specify the profile inside the maven command :
mvn clean install -P integration-tests
<profiles>
<profile>
<id>integration-tests</id>
<build>
<plugins>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.12.2</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<id>start-third-parties</id>
<phase>pre-integration-test</phase>
<configuration>
<target>
<!-- ANT stuff -->
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
<execution>
<id>stop-third-parties</id>
<phase>post-integration-test</phase>
<configuration>
<target>
<!-- ANT stuff -->
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
About the stop scripts
Stop scripts are sometimes not easy to write... but there is always a way to kill a process ! The examples bellow are made for
Kafka (replace
kafka.Kafka by anything that can identify the process you started) :
on Linux
ps ax | grep -i 'kafka.Kafka' | grep -v grep | awk '{print $1}' | xargs kill -SIGTERM
You may have to pay to run it on Mac OS X indeed...
on Windows
wmic process where (commandline like "%%kafka.Kafka%%" and not name="wmic.exe") delete
You only need to double '%' if you want the command to be executed within a .bat file.
Conclusion
This solution might work for a lot of third parties appliations that you might want to run in background in order to pass integration tests on your whole application layers :
- run a queue like Kafka or ZeroMQ that doesn't provide Maven integration yet
- run a database like MySQL, Cassandra (Maven plugin available btw...) or HBase
- run an independent instance of anything : Memcached, ElasticSearch or Solar for example
Tell me if you use it for something else ;-)
Excellent article. It saved me a lot of time.
RépondreSupprimerMerci beaucoup.
Oh, I forgot. I'm actually not using it for starting third party applications. I'm using it for starting the application being built by this maven project itself (to start the application under test). Then, in the test-integration phase, I run the robot tests which will connect to the application using sockets.
RépondreSupprimerThanks a lot! It helped me automating my tests!
RépondreSupprimerI had to change a little bit however, because the command under linux was not executed when "run.command.additionnal.arg" is empty. I just added a legal parameter that would not change the execution like:
and it worked :)
Thanks a lot.. bookmarked!
RépondreSupprimer