Chapter 15. Tips and tricks

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.

What really happens during a test?

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 parse tree

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]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”.

Figure 15.1. Typical table parse tree

Typical table parse tree

Executing tables

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.

Binding columns to class members

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.

Cell operators

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.

Handling data types

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.

[Note]Stuff to remember
  • When tests are hanging and locking resources, you should look for FitServer.exe processes to kill rather than stopping the Java process that runs FitNesse.

  • The parse tree represents the test script in memory. Fixtures modify it to display results.

  • The Fixture class is the main workflow controller and coordinator for tests.

  • Fixture arguments are not accessible to the class constructor so you have to check for them later.

  • Symbols and keywords are handled by CellOperation handlers.

  • The Accessor interface provides an abstraction that allows us to use methods, interfaces and properties in the same way.

  • Most fixtures use the TypeAdapter to convert cell contents into objects. This class looks for a public static Parse method to perform the conversion.

  • Use Runner.exe to debug fixtures from Visual Studio.