Fixtures are the glue that connects the HTML tables with the Software Under Test (SUT). Fixtures always inherit from Fixture or a subclass of Fixture. Fixtures tend to be quite easy to write. If they contain complex logic, it usually means there is an architectural problem somewhere, possibly a feature envy type of code smell.
Writing fixtures, even trivial fixtures, is unfortunately labor intensive. Some attention to cleanly layered architecture can minimize the work substantially. In the best case, one can design generic fixtures that operate with many different application modules without modification.
The Fixture module has one main class and a number of supporting classes. You will normally only subclass the main Fixture class, so I'll deal with Fixture first, and the supporting classes at the end of the document.
Fixture has a number of utility methods that fall into a few categories: navigation, annotation, and general utility.
Annotation methods are the means of reporting the results back to the table. They are responsible for adding the necessary HTML to color the background and add error and exception data.
There are four annotation methods: right(cell), wrong(cell, actual = None), ignore(cell) and exception(cell, exceptionObject). Your fixture calls the appropriate method to annotate the results. To avoid confusion, you should follow the standard definitions.
The annotation methods in Fixture are all proxies for identical annotation methods on the parse cell. In addition, they add the appropriate count to the count object that is displayed by the Summary fixture and the runners.
The annotation methods add a class= attribute to either the td tag or the span tag. They can be switched to add bgcolor= or color= tags instead.
Some of these seem to be utilities that are needed by one or another of the standard fixtures, and were put here to avoid duplication. This class of utility includes _counts, label, gray, greenlabel, and escape. The latter escapes the & and < symbols for HTML.
The camel() routine converts a column label in human readable form to a form that can be used as a method name in Python. Camel is discussed in depth in the Label to Identifier Mapping document.
parse(s, type) was a hook to allow the fixture to do custom parsing. The original version was heavily dependent on Java's reflection mechanism to provide type information; this doesn't work in Python. See the TypeAdapter writeup for the new parse hook.
loadFixture(fixtureName) is the general mechanism for loading a fixture. While it's mostly called directly from Fixture.doTables, it's also called from other places, including ActionFixture.
loadClass(fixtureName) is an alias for loadFixture that is depreciated.
loadFixtureRenamesFromFile(path) is used to load renames into the alias table.
The symbol facility supports the symbol columns of the ColumnFixture and RowFixture, and also the symbol cell handler. The symbol table is cleared at the end of every test.
setSymbol() adds a symbol to the symbol table.
getSymbol() gets the value for a symbol from the symbol table.
getArgs() returns a (possibly empty) list of the text in all cells on the first row of the table following the fixture name.
getArgCells() returns a list of all of the cells on the first row, including the cell containing the fixture name. This call is useful if you need to annotate an arguement, or if you want to find out the name of the fixture. You need to be a bit cautious with this: on one side the fixture name may already contain annotations, on the other it may be a business facing name that is not related to the actual fixture on the file system.
This mechanism does not currently support symbols in the arguement cells; if you want to have arguements that reference symbols, you need to code it yourself.
The arguements are not available in the "__init__" method; the earliest you can access them is in the doTables method.
check(cell, typeAdapter) handles all the check logic, including annotating the cell and tabulating the result. It delegates the check logic to the type adapter's check(cell) routine. It returns a checkResult object.
parseAndSet(cell, typeAdapter) handles the logic required to parse a cell's contents and store the result in a field, method or property. It returns a CheckResult object; the actual result from the parse is bound to the CheckResult object's parseResult attribute.
It differs from the parse routine in TypeAdapter in that it requires a cell; it does not accept a string, and it also supports the exception[] cell handler. The exception[] cell handler, if used, requires the third, value, parameter. Parse cells with this cell handler are annotated and tabulated.
While it's not strictly necessary to use the type adapter mechanism in your own fixtures, it does allow you to abstract type conversions out of your fixtures. The standard fixtures require it. This describes the way you set up the type information for the type adapters. Look at the three standard fixtures to see the interface with the TypeAdapter mechanism.
These are three utility classes that are used by Fixture. Counts encapsulates the standard counts put out by the summary fixture, and also provides a good way of managing them. An instance of the class is plugged into the Fixture subclass that's created for each table, and then the result is rolled up to the version of Counts that's in the main Fixture class. The Counts class is a good example of a class that can be used from the Generic type adapter. It wasn't intended that way; it just grew.
RunTime is a utility class that captures the time it was instantiated, and then pretty prints an elapsed time. It's useful for the summary, if nothing else.
NullFixtureListener is a null object that is replaced by FitServer and TestRunner. See FitServer and FitServerTest to understand what it does.
There are also two classes that support the CSS style sheets needed by the annotation method. One class provides the data needed for FitNesse, the other provides the data needed for Fit.
There are a number of ways of communicating between fixtures.
The cleanest, in the sense of being the most obvious to the reader, is to put a reference to whatever object you're passing into a symbol using the setSymbol(key, value) and getSymbol(key) methods of Fixture. This has the advantage that it will work in a number of different language versions of Fit, and it will also let you use several fixtures (ArrayFixture, RowFixture, SetFixture and SubsetFixture) without having to write a subclass.
The other major method is to use some form of static reference. In Java, this is usually a static variable of a class that you know will always be loaded. In Python, it's usually an identifier in a module, although it can be an identifier in a class object.
Another good strategy is to use the DoFixture from the Fit Library and avoid the problem altogether.