Some FitNesse tricks: Classpath and debugging

how-to
Aug 18, 200818 mins

On my project, we use Maven to build our software and FitNesse to write functional specifications. However, it was obvious that FitNesse wasn’t designed by Maven-fans. When I use Maven, I already have control over my classpath, and specifying it in every FitNesse test gets to be old really fast. Why can’t I just inherit the project class path, and start FitNesse using maven-antrun-plugin or just from my IDE?

I found a neat way to implement this by overriding FitNesse. Using the same technique, I’m also able to debug FitNesse tests.

Taking control of FitNesse

Fixing the classpath is exceedingly simple. But you need to understand a bit of the inner workings of FitNesse to get it to work. When you press the “test” button on a test, or the suite button on a test suite, FitNesse instantiates a class based on the registered “responder”. Our first order of business is to override this, but in order to do that, we need to take control over FitNesse.

You can do this by adding fitnesse.jar and fitlibrary.jar to your classpath, either manually in Eclipse, or by using a Maven dependency on org.fitnesse:fitnesse and org.fitnesse:fitlibrary. The latest version in the Maven repository is 20060719, which is old, but I’ve seen nothing that was worth the hassle of the manual download.

Once FitNesse is in you IDE’s classpath, you can run the Java class “fitnesse.FitNesse”. This starts FitNesse on port 80. Navigate to, say “https://localhost:8080/MyFirstTest”, and you’re ready to add a test.

In order to get it to work, though, you will have to do something like the following:

!path target/classes
!path <HOME>/.m2/repository/org/fitnesse/fitnesse/20060719/fitnesse-20060719.jar
!path <HOME>/.m2/repository/org/fitnesse/fitlibrary/20060719/fitlibrary-20060719.jar

!|my.test.ExampleFixture|
|first|second|sum?|product?|
|10|10|20|100|
|1|0|1|0|
|10000|1|10001|10000|

Ugh! Bad FitNesse!

Taking control of the classpath

After examining the class fitnesse.FitNesse, you will find that there is no simple way to modify the responder. So I basically copied that class into my own main class:

<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> RunFitnesse <span style="color: #66cc66;">{</span>
 
    <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">static</span> <span style="color: #993333;">void</span> main<span style="color: #66cc66;">(</span><span style="color: #aaaadd; font-weight: bold;">String</span> args<span style="color: #66cc66;">[</span><span style="color: #66cc66;">]</span><span style="color: #66cc66;">)</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #aaaadd; font-weight: bold;">Exception</span> <span style="color: #66cc66;">{</span>
        RunFitnesse runFitnesse = <span style="color: #000000; font-weight: bold;">new</span> RunFitnesse<span style="color: #66cc66;">(</span><span style="color: #66cc66;">)</span>;
        runFitnesse.<span style="color: #006600;">start</span><span style="color: #66cc66;">(</span><span style="color: #66cc66;">)</span>;
    <span style="color: #66cc66;">}</span>
 
    <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #993333;">void</span> start<span style="color: #66cc66;">(</span><span style="color: #66cc66;">)</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #aaaadd; font-weight: bold;">Exception</span> <span style="color: #66cc66;">{</span>
        FitNesseContext context = loadContext<span style="color: #66cc66;">(</span><span style="color: #66cc66;">)</span>;
        FitNesse fitnesse = <span style="color: #000000; font-weight: bold;">new</span> FitNesse<span style="color: #66cc66;">(</span>context<span style="color: #66cc66;">)</span>;
        fitnesse.<span style="color: #006600;">applyUpdates</span><span style="color: #66cc66;">(</span><span style="color: #66cc66;">)</span>;
        <span style="color: #993333;">boolean</span> started = fitnesse.<span style="color: #006600;">start</span><span style="color: #66cc66;">(</span><span style="color: #66cc66;">)</span>;
        <span style="color: #b1b100;">if</span><span style="color: #66cc66;">(</span>started<span style="color: #66cc66;">)</span>
           printStartMessage<span style="color: #66cc66;">(</span>context<span style="color: #66cc66;">)</span>;
    <span style="color: #66cc66;">}</span>
 
    <span style="color: #000000; font-weight: bold;">protected</span> FitNesseContext loadContext<span style="color: #66cc66;">(</span><span style="color: #66cc66;">)</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #aaaadd; font-weight: bold;">Exception</span> <span style="color: #66cc66;">{</span>
        FitNesseContext context = <span style="color: #000000; font-weight: bold;">new</span> FitNesseContext<span style="color: #66cc66;">(</span><span style="color: #66cc66;">)</span>;
        ComponentFactory componentFactory =
            <span style="color: #000000; font-weight: bold;">new</span> ComponentFactory<span style="color: #66cc66;">(</span>context.<span style="color: #006600;">rootPath</span><span style="color: #66cc66;">)</span>;
        context.<span style="color: #006600;">port</span> = <span style="color: #cc66cc;">80</span>;
        context.<span style="color: #006600;">rootPath</span> = <span style="color: #ff0000;">"./src/main/fitnesse"</span>;
        context.<span style="color: #006600;">rootPageName</span> = <span style="color: #ff0000;">"FitNesseRoot"</span>;;
        context.<span style="color: #006600;">rootPagePath</span> = context.<span style="color: #006600;">rootPath</span> + <span style="color: #ff0000;">"/"</span> + context.<span style="color: #006600;">rootPageName</span>;
        context.<span style="color: #006600;">root</span> = componentFactory.<span style="color: #006600;">getRootPage</span><span style="color: #66cc66;">(</span>
              FileSystemPage.<span style="color: #006600;">makeRoot</span><span style="color: #66cc66;">(</span>context.<span style="color: #006600;">rootPath</span>, context.<span style="color: #006600;">rootPageName</span><span style="color: #66cc66;">)</span><span style="color: #66cc66;">)</span>;
        context.<span style="color: #006600;">responderFactory</span> = <span style="color: #000000; font-weight: bold;">new</span> ResponderFactory<span style="color: #66cc66;">(</span>context.<span style="color: #006600;">rootPagePath</span><span style="color: #66cc66;">)</span>;
        context.<span style="color: #006600;">logger</span> = <span style="color: #000000; font-weight: bold;">null</span>;
        context.<span style="color: #006600;">authenticator</span> = 
            componentFactory.<span style="color: #006600;">getAuthenticator</span><span style="color: #66cc66;">(</span><span style="color: #000000; font-weight: bold;">new</span> PromiscuousAuthenticator<span style="color: #66cc66;">(</span><span style="color: #66cc66;">)</span><span style="color: #66cc66;">)</span>;
        context.<span style="color: #006600;">htmlPageFactory</span> =
            componentFactory.<span style="color: #006600;">getHtmlPageFactory</span><span style="color: #66cc66;">(</span><span style="color: #000000; font-weight: bold;">new</span> HtmlPageFactory<span style="color: #66cc66;">(</span><span style="color: #66cc66;">)</span><span style="color: #66cc66;">)</span>;
 
        context.<span style="color: #006600;">responderFactory</span>.<span style="color: #006600;">addResponder</span><span style="color: #66cc66;">(</span><span style="color: #ff0000;">"test"</span>, 
            InheritClasspathTestResponder.<span style="color: #000000; font-weight: bold;">class</span><span style="color: #66cc66;">)</span>;
 
 
        <span style="color: #aaaadd; font-weight: bold;">String</span> extraOutput = 
            componentFactory.<span style="color: #006600;">loadResponderPlugins</span><span style="color: #66cc66;">(</span>context.<span style="color: #006600;">responderFactory</span><span style="color: #66cc66;">)</span>;
        extraOutput += componentFactory.<span style="color: #006600;">loadWikiWidgetPlugins</span><span style="color: #66cc66;">(</span><span style="color: #66cc66;">)</span>;
        extraOutput += componentFactory.<span style="color: #006600;">loadContentFilter</span><span style="color: #66cc66;">(</span><span style="color: #66cc66;">)</span>;
        <span style="color: #000000; font-weight: bold;">return</span> context;
    <span style="color: #66cc66;">}</span>
<span style="color: #66cc66;">}</span>

Again: FitNesse really should provide better hooks for this!

The cool part is of course InheritClasspathTestResponder, which overrides the standard FitNesse TestResponder:

<pre>&lt;span style="color: #000000; font-weight: bold;"&gt;public&lt;/span&gt; &lt;span style="color: #000000; font-weight: bold;"&gt;class&lt;/span&gt; InheritClasspathTestResponder &lt;span style="color: #000000; font-weight: bold;"&gt;extends&lt;/span&gt; TestResponder &lt;span style="color: #66cc66;"&gt;{&lt;/span&gt;
 
    @Override
    &lt;span style="color: #000000; font-weight: bold;"&gt;protected&lt;/span&gt; &lt;span style="color: #aaaadd; font-weight: bold;"&gt;String&lt;/span&gt; buildCommand&lt;span style="color: #66cc66;"&gt;(&lt;/span&gt;PageData data, &lt;span style="color: #aaaadd; font-weight: bold;"&gt;String&lt;/span&gt; program,
            &lt;span style="color: #aaaadd; font-weight: bold;"&gt;String&lt;/span&gt; classPath&lt;span style="color: #66cc66;"&gt;)&lt;/span&gt; &lt;span style="color: #000000; font-weight: bold;"&gt;throws&lt;/span&gt; &lt;span style="color: #aaaadd; font-weight: bold;"&gt;Exception&lt;/span&gt; &lt;span style="color: #66cc66;"&gt;{&lt;/span&gt;
        &lt;span style="color: #aaaadd; font-weight: bold;"&gt;String&lt;/span&gt; parentClassPath = &lt;span style="color: #aaaadd; font-weight: bold;"&gt;System&lt;/span&gt;.&lt;span style="color: #006600;"&gt;getProperty&lt;/span&gt;&lt;span style="color: #66cc66;"&gt;(&lt;/span&gt;&lt;span style="color: #ff0000;"&gt;"java.class.path"&lt;/span&gt;&lt;span style="color: #66cc66;"&gt;)&lt;/span&gt;;
        &lt;span style="color: #aaaadd; font-weight: bold;"&gt;String&lt;/span&gt; pathSeparator = &lt;span style="color: #aaaadd; font-weight: bold;"&gt;System&lt;/span&gt;.&lt;span style="color: #006600;"&gt;getProperty&lt;/span&gt;&lt;span style="color: #66cc66;"&gt;(&lt;/span&gt;&lt;span style="color: #ff0000;"&gt;"path.separator"&lt;/span&gt;&lt;span style="color: #66cc66;"&gt;)&lt;/span&gt;;
        &lt;span style="color: #aaaadd; font-weight: bold;"&gt;String&lt;/span&gt;&lt;span style="color: #66cc66;"&gt;[&lt;/span&gt;&lt;span style="color: #66cc66;"&gt;]&lt;/span&gt; classPathElements = parentClassPath.&lt;span style="color: #006600;"&gt;split&lt;/span&gt;&lt;span style="color: #66cc66;"&gt;(&lt;/span&gt;pathSeparator&lt;span style="color: #66cc66;"&gt;)&lt;/span&gt;;
        &lt;span style="color: #b1b100;"&gt;for&lt;/span&gt; &lt;span style="color: #66cc66;"&gt;(&lt;/span&gt;&lt;span style="color: #aaaadd; font-weight: bold;"&gt;String&lt;/span&gt; element : classPathElements&lt;span style="color: #66cc66;"&gt;)&lt;/span&gt; &lt;span style="color: #66cc66;"&gt;{&lt;/span&gt;
            classPath += pathSeparator + &lt;span style="color: #ff0000;"&gt;"&lt;span style="color: #000099; font-weight: bold;"&gt;"&lt;/span&gt;"&lt;/span&gt; + element + &lt;span style="color: #ff0000;"&gt;"&lt;span style="color: #000099; font-weight: bold;"&gt;"&lt;/span&gt;"&lt;/span&gt;;
        &lt;span style="color: #66cc66;"&gt;}&lt;/span&gt;
        &lt;span style="color: #000000; font-weight: bold;"&gt;return&lt;/span&gt; &lt;span style="color: #000000; font-weight: bold;"&gt;super&lt;/span&gt;.&lt;span style="color: #006600;"&gt;buildCommand&lt;/span&gt;&lt;span style="color: #66cc66;"&gt;(&lt;/span&gt;data, program, classPath&lt;span style="color: #66cc66;"&gt;)&lt;/span&gt;;
    &lt;span style="color: #66cc66;"&gt;}&lt;/span&gt;
&lt;span style="color: #66cc66;"&gt;}&lt;/span&gt;</pre>

Running your new main class instead of fitnesse.FitNesse will add the current class path to the executing FitNesse class path. This means an end to monkeying around with FitNesse classpath.

In order for suites to work, you also have to override SuiteResponder identically to TestResponder.

<h3>Debugging FitNesse</h3>

Add the following line after registering the test responder:

<pre>context.&lt;span style="color: #006600;"&gt;responderFactory&lt;/span&gt;.&lt;span style="color: #006600;"&gt;addResponder&lt;/span&gt;&lt;span style="color: #66cc66;"&gt;(&lt;/span&gt;&lt;span style="color: #ff0000;"&gt;"debug"&lt;/span&gt;, DebugTestResponder.&lt;span style="color: #000000; font-weight: bold;"&gt;class&lt;/span&gt;&lt;span style="color: #66cc66;"&gt;)&lt;/span&gt;;</pre>

Here is DebugTestResponder:

<pre>&lt;span style="color: #000000; font-weight: bold;"&gt;public&lt;/span&gt; &lt;span style="color: #000000; font-weight: bold;"&gt;class&lt;/span&gt; DebugTestResponder &lt;span style="color: #000000; font-weight: bold;"&gt;extends&lt;/span&gt; TestResponder &lt;span style="color: #66cc66;"&gt;{&lt;/span&gt;   &lt;span style="color: #000000; font-weight: bold;"&gt;private&lt;/span&gt; &lt;span style="color: #000000; font-weight: bold;"&gt;static&lt;/span&gt; &lt;span style="color: #000000; font-weight: bold;"&gt;final&lt;/span&gt; &lt;span style="color: #993333;"&gt;int&lt;/span&gt; DEBUG_PORT = &lt;span style="color: #cc66cc;"&gt;1044&lt;/span&gt;;   @Override &lt;span style="color: #000000; font-weight: bold;"&gt;protected&lt;/span&gt; &lt;span style="color: #aaaadd; font-weight: bold;"&gt;String&lt;/span&gt; buildCommand&lt;span style="color: #66cc66;"&gt;(&lt;/span&gt;PageData data, &lt;span style="color: #aaaadd; font-weight: bold;"&gt;String&lt;/span&gt; program, &lt;span style="color: #aaaadd; font-weight: bold;"&gt;String&lt;/span&gt; classPath&lt;span style="color: #66cc66;"&gt;)&lt;/span&gt; &lt;span style="color: #000000; font-weight: bold;"&gt;throws&lt;/span&gt; &lt;span style="color: #aaaadd; font-weight: bold;"&gt;Exception&lt;/span&gt; &lt;span style="color: #66cc66;"&gt;{&lt;/span&gt; &lt;span style="color: #aaaadd; font-weight: bold;"&gt;String&lt;/span&gt; parentClassPath = &lt;span style="color: #aaaadd; font-weight: bold;"&gt;System&lt;/span&gt;.&lt;span style="color: #006600;"&gt;getProperty&lt;/span&gt;&lt;span style="color: #66cc66;"&gt;(&lt;/span&gt;&lt;span style="color: #ff0000;"&gt;"java.class.path"&lt;/span&gt;&lt;span style="color: #66cc66;"&gt;)&lt;/span&gt;; &lt;span style="color: #aaaadd; font-weight: bold;"&gt;String&lt;/span&gt; pathSeparator = &lt;span style="color: #aaaadd; font-weight: bold;"&gt;System&lt;/span&gt;.&lt;span style="color: #006600;"&gt;getProperty&lt;/span&gt;&lt;span style="color: #66cc66;"&gt;(&lt;/span&gt;&lt;span style="color: #ff0000;"&gt;"path.separator"&lt;/span&gt;&lt;span style="color: #66cc66;"&gt;)&lt;/span&gt;; &lt;span style="color: #aaaadd; font-weight: bold;"&gt;String&lt;/span&gt;&lt;span style="color: #66cc66;"&gt;[&lt;/span&gt;&lt;span style="color: #66cc66;"&gt;]&lt;/span&gt; classPathElements = parentClassPath.&lt;span style="color: #006600;"&gt;split&lt;/span&gt;&lt;span style="color: #66cc66;"&gt;(&lt;/span&gt;pathSeparator&lt;span style="color: #66cc66;"&gt;)&lt;/span&gt;; &lt;span style="color: #b1b100;"&gt;for&lt;/span&gt; &lt;span style="color: #66cc66;"&gt;(&lt;/span&gt;&lt;span style="color: #aaaadd; font-weight: bold;"&gt;String&lt;/span&gt; element : classPathElements&lt;span style="color: #66cc66;"&gt;)&lt;/span&gt; &lt;span style="color: #66cc66;"&gt;{&lt;/span&gt; classPath += pathSeparator + &lt;span style="color: #ff0000;"&gt;"&lt;span style="color: #000099; font-weight: bold;"&gt;"&lt;/span&gt;"&lt;/span&gt; + element + &lt;span style="color: #ff0000;"&gt;"&lt;span style="color: #000099; font-weight: bold;"&gt;"&lt;/span&gt;"&lt;/span&gt;; &lt;span style="color: #66cc66;"&gt;}&lt;/span&gt; &lt;span style="color: #aaaadd; font-weight: bold;"&gt;String&lt;/span&gt; debugOptions = &lt;span style="color: #ff0000;"&gt;"-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address="&lt;/span&gt; + DEBUG_PORT; &lt;span style="color: #000000; font-weight: bold;"&gt;return&lt;/span&gt; &lt;span style="color: #ff0000;"&gt;"java "&lt;/span&gt; + debugOptions + &lt;span style="color: #ff0000;"&gt;" "&lt;/span&gt; + &lt;span style="color: #ff0000;"&gt;"-cp "&lt;/span&gt; + classPath + &lt;span style="color: #ff0000;"&gt;" "&lt;/span&gt; + program; &lt;span style="color: #66cc66;"&gt;}&lt;/span&gt; &lt;span style="color: #66cc66;"&gt;}&lt;/span&gt;</pre>

To use it, run the test in FitNesse as usual, but then replace the “?test” part with “?debug”. FitNesse will now start running, but then freeze, while it is waiting for a debugger to connect. Connect a remote debugger from your IDE to the debug port (1044) in my example. In Eclipse, this is done by selecting Run -&gt; Open Debug Dialog, right click “Remote Java Application” and choose new.

<h3>In conclusion</h3>

FitNesse has a powerful mechanism for overriding the default behavior built in to the ResponderFactory framework. It is a bit hard to stuff things into the framework until you learn how to do it. I demonstrated how by replacing the “test” and “suite” verbs, we can address one of the most common complaint I get from FitNesse users: Class path struggles. But adding a “debug” verb, we can make a test suspend at start up while waiting for us to attach a remote debugger.

Both these techniques have been huge time savers for me. I hope you like them, too.