Understanding what really happens during a test allows you to customise FIT and FitNesse to your particular project needs and troubleshoot tests. Now that we've learnt how to use FitNesse, we take a peek under the hood and examine how the FIT engine really works and look at some more advanced usages of FitSharp.
FitNesse is just a user interface to FIT. It prepares test
pages by converting wiki
syntax to HTML, but it does not execute tests internally.
Instead, it calls an external FIT runner to execute
the tests. FitNesse starts a Java FIT runner by default,
but we can change this by modifying the
TEST_RUNNER
variable. (We did this in
How FitNesse connects to .NET classes:
we changed the runner to
dotnet2\Runner.exe.)
In a .NET environment, FitNesse starts an external
Runner.exe process and passes the runner class name, test name and FitNesse
port as parameters. The Runner process then connects to FitNesse,
downloads the page and executes
tests. The results are passed back to
FitNesse, which displays them to the user. This is why, when
tests are hanging and locking resources, you should look for
Runner.exe processes to kill rather
than stopping the Java process that runs FitNesse.
There are a few other test runners classes that you can use. In
Chapter 10, Working in a team,
we used TestRunner
to execute tests from a NAnt script.
You can also use
FolderRunner
to execute tests from HTML files stored on disk (see http://www.syterra.com/Fit/UsingFitnesse.html).
The test runner first breaks the page into HTML tables, then
analyses the tables and creates a
parse tree.
A parse tree is a dynamic representation of the test
tables. As the tests are executed,
fixtures modify the tree by adding
comments (“expected something,
actual something else”) or creating
completely new cells (surplus elements in a
RowFixture
query). At the end of the tests, FitNesse uses the tree contents to
display the results.
![]() | Important |
|---|---|
You can see the full source code for all FitSharp classes described in this chapter on http://github.com/jediwhale/fitsharp |
Parse tree elements are instances of the
Parse
class.
These elements build a tree by creating linked lists with two
properties: More and Parts.
More
points to the next element on the same level of the
hierarchy and
Parts
points to the first child element.
The Tag
property contains the HTML tag name, for example
table,
tr and td.
The Body
property contains the cell content for
td
tags, and is typically empty for all other
tags. The parse tree
for the table from Figure 2.2, “FitNesse creates a new page for the Hello World test” is shown in Figure 15.1, “Typical table parse tree”.
The
Fixture
class
is the main workflow controller and coordinator for
tests. It also defines the standard
interface for test classes and provides
default method implementations. In order to
enable subclasses to adjust the test execution, this
class contains a lot of virtual methods. Fixtures
can implement their own table format and test workflow
by overriding these methods.
After the test runner has created the parse tree, an instance of
the
Fixture
class is created and the tree is passed to its
DoTables
method. The main
Fixture
then takes the class name from the first cell in the first
table and creates an instance of this class. The
ProcessTables
method of this instance is then called, and the
whole parse tree is passed to it.
Instantiating another Fixture
and then passing the whole
tree may seem a bit weird, but this is how
DoFixture and
similar flow-mode FitLibrary fixtures take over page
processing.
In the default implementation, which most fixtures do
not override,
ProcessTables
just iterates over tables (first level of the
tree). Each table is processed by loading the class name
from the first cell, instantiating the class, loading
arguments from the remaining cells in the first row and
calling the
DoTable
method of the new fixture. The appropriate
table subtree is passed
to this DoTable
method.
Note that arguments are not passed
to the constructor, so they have to be loaded after the
instantiation. If you
create your own Fixture
subclass, remember that
fixture arguments are not accessible to the class
constructor so you have to check for them later.
DoTable is a good
candidate for this.
FitLibrary fixtures turn on flow mode (see
Embed fixtures for best results)
by overriding ProcessTables
and then processing the rest
of the page as a single big table.
The default implementation of the
DoTable method
calls the
DoRows
method, passing the pointer to the second
table row (so DoRows
does not receive the first row with
fixture type and arguments).
The default
DoRows
implementation iterates through child elements of the
parse subtree, which represent table rows, and calls
DoRow
for each one of them.
The default DoRow
calls the
DoCells method, and this
iterates through cells and calls
DoCell
for each one of them. DoCells also checks
whether there was
an exception during cell processing or not.
Exceptions are recorded in the parse tree by
calling the
Exception
method.
By default, DoCell
just marks the cell as ignored, by calling the
Ignore method.
As you can see, there are quite a few opportunities for subfixtures to take over and implement their particular test workflow. This is why FIT and FitNesse are so flexible.
In addition to
Exception
and
Ignore,
there are two more methods that can be called to mark part of a parse
tree:
Right
should be called if a test succeeds, and
Wrong
should be called if a test fails, optionally passing
the actual results, so that they can be displayed.
Let's examine one use case in a bit more detail.
ColumnFixture
is a general purpose testing class, which we
introduced in
Chapter 2, Installing FitNesse,
and explained in more detail in
Chapter 4, Writing basic tests.
It uses the second table row to map columns to
properties, methods and fields. The other rows
are used as test inputs and expected results.
Several other classes,
including
RowFixture,
also use the header row to map
test object properties to columns. This common functionality
that binds columns to object properties is
encapsulated into a common superclass,
BoundFixture
.
BoundFixture
provides a way to bind columns to class members and
overrides
DoCell
to execute the appropriate cell operation. It does not
specify how the columns are bound to fields, but
expects
subclasses to fill in the required mapping.
BoundFixture
also overrides
DoRow
in order to provide one more extension point:
Reset. This
method is called before each row is processed, allowing
subclasses to clear temporary data before the next test
execution.
ColumnFixture
takes over after the first table row is processed, so it
overrides the
DoRows
method. It uses the current row (second row in the
table) to bind columns to properties
of the target class and then calls the BoundFixture
DoRows
method. However, it passes the next row (third one in the table) as
the argument. From this point on, the
method DoCell method of BoundFixture
is called for each cell,
and it either puts in data
to set up tests or compares expected and actual values
to verify results.
Fixtures use cell operators
to access target class properties, methods and fields. This is where symbols
(see Use symbols to pass dynamic information) and keywords like
exception or blank
(see
Checking for errors and
Fixture keywords)
come into play.
fit.Service.Service
class has a static list of cell operators.
In order to execute a cell operation, the test runner examines
the list and asks operators whether they match cell
content and data type. The first matching
handler is used to execute the cell. For example,
ParseSymbol
matches cells starting with <<
and
ExecuteException
matches cells with the
exception
keyword.
You can load and unload operators dynamically to change the way cells are processed (see Load non-standard cell operators for simpler comparisons).
Because FitLibrary classes do not use
CellOperation
handlers by default, these features are not available in
DoFixture
and similar classes unless you specifically ask for them.
By default, FitSharp
looks for a
public static
Parse method
in the target class and uses it to perform the
conversion from a string in test tables to a typed .NET value. It also handles arrays of objects by
splitting the comma-separated contents into individual
objects and then calling the
Parse
method to process them.
So all you need to do to make FitNesse understand your new data type as a cell value is implement
a public static Parse(String value) method on it. Of course, making ToString
an inverse operation will simplify troubleshooting and make reports nicer.
![]() | Stuff to remember |
|---|---|
|

![[Important]](../images/resources/important.png)

![[Note]](../images/resources/note.png)


