Showing FitNesse Test Results in Hudson

Have you tried to get your FitNesse reports into Hudson?

Although it’s simple to get an Ant task to run the tests, and fail the build, it would be nice to see the results in Hudson’s junit report.
Unfortunately FitNesse outputs it’s results in a format that Hudson doesn’t understand. But that’s OK, because it’s all XML.

I knocked together a really quick Proof of Concept xsl and ant script that allow you to run the FitNesse tests and convert the output to the Ant junit format that Hudson understands.

It’s a work in progress, but it should be enough to get you going. Watch this space.
I ran it against one suite and Hudson picked up the tests (and failure) ok.

This is the build.xml that I found on Naresh Jain’s blog about integrating FitNesse with CruiseControl, with the convert target added

<?xml version="1.0" encoding="UTF-8"?>
<project name="Acceptance_Tests-Common" default="test">
  <target name="smoke" description="Run fitnesse acceptance tests.">
    <property name="fitnesse.output.dir" value="build" />
    <property name="fitnesse.output.file" value="${fitnesse.output.dir}/fitnesse-test-results" />
    <property name="fitnesse.port" value="8765" />
    <path id="fitpath">
      <fileset dir=".">
        <include name="fit*.jar" />
        <include name="lib/fitnesse-20070619/fitnesse-20070619.jar" />
      </fileset>
    </path>
    <echo message="About to run fitnesse server" level="info" />
    <parallel>
      <daemons>
        <java classname="fitnesse.FitNesse" classpath="${toString:fitpath};${ant.home}/lib/xercesImpl.jar">
          <arg value="-p" />
          <arg value="${fitnesse.port}" />
          <arg value="-e" />
          <arg value="0" />
          <arg value="-d" />
          <arg value="content" />
          <arg value="-r" />
          <arg value="FitnesseRoot" />
        </java>
      </daemons>
      <sequential>
        <echo message="sleeping for 10 seconds to let FitNesse server start" level="info" />
        <sleep seconds="10" />
        <java classpathref="fitpath" classname="fitnesse.runner.TestRunner" fork="true" resultproperty="fit.test.failures">
          <arg value="-debug" />
          <arg value="-xml" />
          <arg value="${fitnesse.output.file}.xml" />
          <arg value="-html" />
          <arg value="${fitnesse.output.file}.html" />
          <arg value="localhost" />
          <arg value="${fitnesse.port}" />
          <arg value="UserStories.FooterStory" />
        </java>
        <replace file="${fitnesse.output.file}.html" token="<base href="http://localhost:${fitnesse.port}/"/>" />
        <echo message="Finished FIT tests: ${fit.test.failures} failures/exceptions" level="info" />
        <!--
	  <fail message="FIT test failures/exceptions: ${fit.test.failures}">
	    <condition>
	      <not>
	        <equals arg1="${fit.test.failures}" arg2="0" />
	      </not>
            </condition>
	  </fail>
	  -->
      </sequential>
    </parallel>
  </target>

  <target name="convert-fitnesse-results-to-junit">
    <xslt style="fitnesse2junit.xsl" in="build/fitnesse-test-results.xml" out="build/TEST-fitnesse.xml" />
  </target>

  <target name="test" depends="smoke, convert-fitnesse-results-to-junit" />
</project>

A slight update on the XSL. This one creates the correct nodes for exceptions and errors (although it appears to make no difference to Hudson)

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
  <xsl:element name="testsuite">
    <xsl:attribute name="tests">
      <xsl:value-of select="sum(testResults/finalCounts/*)" />
    </xsl:attribute>
    <xsl:attribute name="failures">
      <xsl:value-of select="testResults/finalCounts/wrong" />
    </xsl:attribute>
    <xsl:attribute name="disabled">
      <xsl:value-of select="testResults/finalCounts/ignores" />
    </xsl:attribute>
    <xsl:attribute name="errors">
      <xsl:value-of select="testResults/finalCounts/exceptions" />
    </xsl:attribute>
    <xsl:attribute name="name">AcceptanceTests</xsl:attribute>
  <xsl:for-each select="testResults/result">
    <xsl:element name="testcase">
      <xsl:attribute name="classname">
        <xsl:value-of select="/testResults/rootPath" />
      </xsl:attribute>
      <xsl:attribute name="name">
        <xsl:value-of select="relativePageName" />
      </xsl:attribute>
      <xsl:choose>
        <xsl:when test="counts/exceptions > 0">
          <xsl:element name="error">
            <xsl:attribute name="message">
              <xsl:value-of select="counts/exceptions" />
              <xsl:text> exceptions thrown</xsl:text>
              <xsl:if test="counts/wrong > 0">
                <xsl:text> and </xsl:text>
                <xsl:value-of select="counts/wrong" />
                <xsl:text> assertions failed</xsl:text>
              </xsl:if> 
            </xsl:attribute>
          </xsl:element>
        </xsl:when>
        <xsl:when test="counts/wrong > 0">
          <xsl:element name="failure">
            <xsl:attribute name="message">
              <xsl:value-of select="counts/wrong" />
              <xsl:text> assertions failed</xsl:text>
            </xsl:attribute>
          </xsl:element>
        </xsl:when>
      </xsl:choose>
    </xsl:element>
  </xsl:for-each>
  </xsl:element>
</xsl:template>
</xsl:stylesheet>