Dependency management in FitNesse with Apache Ivy

Arjan Molenaar

In a software project dependency management is default practice. So what about dependency management in your FitNesse acceptance test suite?

In my previous post I explained how you can make FitNesse and Maven work together. If you're not into Maven, but want to handle (Java) project dependencies in a convenient way, you're probably using Ivy. Ivy is used by Gradle and SBT under the hood, but not packages with Ant by default. It's a neat tool, it does dependency management very well. It is compatible with Maven's POM files: it can read them as well as write them. In this post I'll use Ant as a basis.

Ant does not have a fixed set of phases, as compared to Maven. Although this can make your build scripts very complicated, it allows for a lot of flexibility.

In short:

  • Dependency management is easy enough with Ant.
  • Ant gives you all freedom to arrange the build the way you like.
  • Ivy lays no restrictions on the number of dependency configurations, so we can do fine grained dependency management.

Enable dependency management in Ant

(If you're not using Ant, just skip ahead.)

So the first challange is to enable Ivy from Ant. You can not expect everyone to install Ivy by hand before even starting to build your application. Luckely Ivy can be bootstrapped from Ant fairly easy:

<project name="Fitnesse" default="all" basedir="."
	xmlns:ivy="antlib:org.apache.ivy.ant">

  <!--
    All your tasks go here.
    -->
    
  <!--
    Dependency management boilerplate:
    -->
  <property name="maven.central.url" value="http://repo2.maven.org/maven2" />

  <available file="antlib/ivy.jar" type="file" property="have.ivy.jar"/>

  <target name="ivy-init" depends="ivy-download">
    <path id="ivy.class.path">
      <fileset dir="antlib">
        <include name="*.jar"/>
      </fileset>
    </path>
    <taskdef resource="org/apache/ivy/ant/antlib.xml"
            uri="antlib:org.apache.ivy.ant"
            classpathref="ivy.class.path" />
  </target>

  <target name="ivy-download" unless="have.ivy.jar">
    <mkdir dir="antlib" />
    <get src="${maven.central.url}/org/apache/ivy/ivy/2.3.0/ivy-2.3.0.jar"
            dest="antlib/ivy.jar" usetimestamp="true" verbose="true" />
  </target>
  
</project>

Ivy can be found in the Maven Central Repository. That makes life a lot easier. The above snippet downloads Ivy if required (ivy-download target) and initialises Ivy tasks (ivy-init). The Ivy tasks are initialised in the namespace antlib:org.apache.ivy.ant, which is mapped to the ivy namespace prefix. It's all XML after all.

Targets requiring dependency information should depend on the ivy-init target.

Ivy needs to know about the repositories. By default it does not know about the maven2 central repo, so Ivy has to be told where it is. This can be done in ivysettings.xml:

<ivysettings>
  <settings defaultResolver="default" />
  <resolvers>
    <chain name="default">
      <ibiblio name="maven2" root="http://repo2.maven.org/maven2" m2compatible="true" />
    </chain>
  </resolvers>
</ivysettings>

Finally the ivy.xml file itself. Ivy has no predefined set of stages, so you can create any configuration you want, default being the default configuration.

<ivy-module version="2.0">
  <info organisation="org.example" module="example" />
  <configurations>
    <conf name="default" visibility="public" />
    <conf name="fitnesse" visibility="private" extends="default"
          description="config used to launch FitNesse" />
    <conf name="acceptance" visibility="public" extends="default"
          description="config used to launch acceptance tests from FitNesse" />
  </configurations>
  <dependencies>
    <!-- project dependencies -->
    <dependency org="org.fitnesse" name="fitnesse" rev="20130530"
                conf="fitnesse->default, acceptance->default" />
  </dependencies>
</ivy-module>

Here a special configurations fitnesse and acceptance are created. This configurations map to the default configuration of FitNesse.

Launch FitNesse from Ant

To use our configuration from Ant, I do:

<project name="Fitnesse" default="all" basedir="."
	xmlns:ivy="antlib:org.apache.ivy.ant">
	
  <target name="run-fitnesse" depends="resolve">
    <ivy:cachepath pathid="fitnesse.classpath" conf="fitnesse" />
    <java classpathref="fitnesse.classpath" classname="fitnesseMain.FitNesseMain" fork="true" failonerror="true">
      <arg value="-p" />
      <arg value="${fitnesse.port}" />
    </java>
  </target>

  <target name="resolve" depends="ivy-init">
    <ivy:resolve />
  </target>
  
  ...
</project>

Now FitNesse can be started from Ant, with the command ant run-fitnesse.

Not using Ant

In case you're using SBT, the tool can generate an ivy.xml from it's own build format, although I have not tried that myself. The generated file can be used as input for the fitnesse-ivy-classpath plugin. For Gradle a separate plugin is available.

Ivy support in FitNesse

The way to deal with Ivy dependencies in FitNesse is similar to the way we deal with Maven dependencies in FitNesse: include a plugin that provides that support.

In case of Ivy there is the fitnesse-ivy-classpath plugin. Let's include that in our ivy.xml:

<dependency org="org.fitnesse.plugins" name="fitnesse-ivy-classpath" rev="0.1"
                conf="fitnesse->default"/>

We need to tell FitNesse we want to add the command from the plugin to FitNesse. Let's add a plugins.properties with the following content:

SymbolTypes = fitnesse.wikitext.widgets.IvyClasspathSymbolType

Start FitNesse and the new "symbol type" will show up:

    run-fitnesse:
      FitNesse (v20121220) Started...
      	port:              9000
      	root page:         fitnesse.wiki.FileSystemPage at ./FitNesseRoot
      	logger:            none
      	authenticator:     fitnesse.authentication.PromiscuousAuthenticator
      	page factory:      fitnesse.responders.PageFactory
      	page theme:        bootstrap
      	page version expiration set to 14 days.
      	Custom symbol types loaded:
      		fitnesse.wikitext.widgets.IvyClasspathSymbolType

Remember we defined a special configuration for acceptance tests? The plugin can be configured to use only what's required for acceptance tests. In a (new) wiki page, add the following line:

!resolve -c acceptance ivy.xml

This will resolve dependencies for the "acceptance" configuration. Reload the wiki page and you'll see FitNesse (and its dependencies) show up.

Conclusion

Doing dependency management from FitNesse for your (Java) project is not hard. I would definately recommend it. You'll save yourself a lot of hassle when you build your project with either Maven or Ant+Ivy, using the same standards you have for your other software modules.

Comments (0)

    Add a Comment