FitNesse and dependency management with Maven

04 Mar, 2013
Xebia Background Header Wave

As a software developer you're using dependency management to handle dependencies on your project; include frameworks and libraries to your project. If you're a Java developer you're probably using Maven. When you're not using Maven you're probably using one of the more versatile build tools like Ant or Gradle, both can use Ivy for dependency management. Either way, you're not putting binaries (jars) in your source control repository.

How about your FitNesse acceptance suite? Since it's all software and all belongs to the project, you probably want to have the same standards when executing the acceptance test suite. It's really not that different from executing your regular (unit) tests.

In this blog I'll explain how to launch a FitNesse suite from Maven. I'll also elaborate on how to get FitNesse to recognize the dependencies required to launch the application. A future post will be dedicated to the FitNesse/Ivy combo.

Launching FitNesse from Maven

When you start integration FitNesse through Maven, there are a few challenges ahead:

  1. Maven has a limited set of scopes in which you can define dependencies (compile, test, runtime)
  2. Maven is using a fixes set of execution phases.
  3. I'd like to start FitNesse in 2 modes: interactive and non-interactive.

Since FitNesse is about acceptance tests it makes sense to execute those in the integration-test phase. The integration-test phase is executed after the package stage. In interactive mode I'm not interested in a package, so I'll launch FitNesse from the test stage directly. The way to make this possible is by using profiles.

FitNesse in interactive mode

First things first: in order to do anything with FitNesse we need to include the dependency. To start FitNesse we'll use the runtime scope:

[sourcecode lang="xml"]<dependencies> <!-- project dependencies go here ... --> <dependency> <groupId>org.fitnesse</groupId> <artifactId>fitnesse</artifactId> <version>20121220</version> <scope>runtime</scope> </dependency> </dependencies> [/sourcecode]

This goes in the normal dependency section.

Now let's look at the profile. As said before, for interactive editing I'd like to start FitNesse from the test phase.

[sourcecode lang="xml"]<profiles> <profile> <id>fitnesse</id> <build> <plugins> <plugin> <artifactId>maven-antrun-plugin</artifactId> <version>1.6</version> <executions> <execution> <id>start-fitnesse</id> <phase>test</phase> <configuration> <tasks> <echo taskname="fitnesse" message="Starting FitNesse..." /> <java classname="fitnesseMain.FitNesseMain" classpathref="maven.runtime.classpath" fork="true"> <arg line="-p 8000" /> <arg line="-d ." /> </java> </tasks> </configuration> <goals> <goal>run</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </profile> </profiles> [/sourcecode]

Mind the classpathref attribute on the Java tag. It's referencing the runtime classpath definition from Maven. That easy.

This will allow you to start FitNesse using the following command:

[sourcecode]$ mvn -Pfitnesse test [/sourcecode]

Quite okay, right? You can start the wiki in stand-alone mode and use it as, well, a wiki.

Maven support in FitNesse

Now let's see if we can make FitNesse import some dependencies through Maven. The dependencies are stated in the pom.xml file, so it makes no sense duplicating those. Since dependencies change over time it's quite labour intensive to manage them by hand it FitNesse. The way to go is to include the maven-classpath-plugin. This plugin needs to be loaded along with the FitNesse binary, so FitNesse can make use of it. Let's include it in the profile, then the profile definition looks like this:

[sourcecode lang="xml"]<profiles> <profile> <id>fitnesse</id> <build> <plugins> <plugin> <artifactId>maven-antrun-plugin</artifactId> <version>1.6</version> <executions> <execution> <id>start-fitnesse</id> <phase>test</phase> <configuration> <tasks> <echo taskname="fitnesse" message="Starting FitNesse..." /> <java classname="fitnesseMain.FitNesseMain" classpathref="maven.runtime.classpath" fork="true"> <arg line="-p 8000" /> <arg line="-d ." /> </java> </tasks> </configuration> <goals> <goal>run</goal> </goals> </execution> </executions> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>org.fitnesse.plugins</groupId> <artifactId>maven-classpath-plugin</artifactId> <version>1.6</version> <scope>runtime</scope> </dependency> </dependencies> </profile> </profiles> [/sourcecode]

That's it for the dependency. Now let's tell FitNesse about it. In a file named (in the same directory where your POM is located) we can define the plugin:

[sourcecode]# Configuration for FitNesse SymbolTypes = fitnesse.wikitext.widgets.MavenClasspathSymbolType [/sourcecode]

We'll define our dependencies from a wiki page, hence define the plugin as SymbolType.

Now let's relaunch FitNesse (Ctrl-C to terminate the running instance). It gives a message saying the plugin is loaded:

[sourcecode] [fitnesse] Starting FitNesse 20121220... (Selenium 2.29.1) FitNesse (v20121220) Started... port: 8000 root page: at ./FitNesseRoot logger: none authenticator: fitnesse.authentication.PromiscuousAuthenticator page factory: fitnesse.responders.PageFactory page theme: bootstrap page version expiration set to 0 days. Custom symbol types loaded: fitnesse.wikitext.widgets.MavenClasspathSymbolType [/sourcecode]

All that has to be done now is including the Maven classpath in hierarchy of the test suite. In a wiki page, define:

[sourcecode]!pomFile pom.xml [/sourcecode]

If you save the file you should see a lot of "classpath" entries show up on the page.

By default it will load the test scope (this is not related to the fact that we started FitNesse from the test phase). The plugin will display all dependencies.

One remark: why did I add the FitNesse dependency to the project dependencies and the plugin to the profile dependencies? You'll need the fitnesse jar once you start executing tests. The plugin is not able to enable profiles at this moment. In practice this is hardly a problem.

Running integration tests

Executing FitNesse as part of the build cycle basically follows the same scheme. Let's put it in a profile that's active by default. This adds some flexibility in that the profile can be disabled to prevent the tests from running (for a local build or something).

[sourcecode lang="xml"]<profiles> <!-- fitnesse profile omitted for clearity --> <profile> <id>fitnesse-integration</id> <activation> <activeByDefault>true</activeByDefault> </activation> <build> <plugins> <plugin> <artifactId>maven-antrun-plugin</artifactId> <version>1.6</version> <executions> <execution> <id>start-fitnesse-integration</id> <phase>integration-test</phase> <configuration> <tasks> <echo taskname="fitnesse" message="Starting FitNesse..." /> <java classname="fitnesseMain.FitNesseMain" classpathref="maven.runtime.classpath" fork="true" failonerror="true"> <arg line="-p 8001" /> <arg line="-c FitNesse.SuiteAcceptanceTests?suite&amp;amp;format=text" /> <arg line="-d ." /> </java> </tasks> </configuration> <goals> <goal>run</goal> </goals> </execution> </executions> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>org.fitnesse.plugins</groupId> <artifactId>maven-classpath-plugin</artifactId> <version>1.6</version> <scope>runtime</scope> </dependency> </dependencies> </profile> </profiles> [/sourcecode]

Where I put FitNesse.SuiteAccceptanceTests it should point to your acceptance suite. The -c option is used to execute a command from the command line. The commands have the same format as the URL would have when you execute it in interactive mode. Hence the ?suite&format=text.

Now the integration tests are executed autoamtically when you perform a mvn install.

Note: if your application is a webapp, you can choose to launch it in the pre-integration-test phase and shut it down in the post-integration-test phase. The de-facto web server to use is Jetty, but that's out of the scope of this blog. In the Xebium project, you can find a complete example of a POM file.

Dealing with Maven Home

By default Maven will look in $HOME/.m2/ for its settings.xml. When you have your settings on a different location you need to have the M2_HOME environment variable set to that location. The Maven classpath plugin takes into account the M2_HOME variable as of version 1.6.

Explore related posts