Fitter Tutorial
Workflow
The typical workflow when using Fitter is to
- Write one or more test documents
- Write any fixtures required by the test documents.
- Run the tests
Whether you write your test documents before you write your fixtures is up to you. Those of us who have been using Fitter for a while have found that it helps to clarify the actual inputs and outputs of the fixtures that the test might need to use.
Writing Test Documents
Test documents are HTML or Word documents containing tables that identify the fixtures that need to be run. Test authors can use whatever editors they’re comfortable with to write and edit the documents. For HTML documents, any text editor can be used, but an editor that offers WYSIWYG editing might be easier than editing the raw HTML. For Word documents, an application like Microsoft Word is required. Microsoft Word can even edit HTML documents which is why Happier (the visual test runner) uses it, by default, to edit both Word and HTML documents.
Test documents can contain any text that helps the test author describe the functionality being exercised. All such text is ignored. The only parts of the test document that Fitter cares about are the tables. Tables are used to identify and run fixtures.
An example test document might look like this:
The text above and the below the table is completely ignored by Fitter. The table isn’t formatted in any specific way, but it could have been formatted in order to make it more visually appealing. Fitter ignores all formatting and only cares about the structure and contents of the table.
After formatting the table to add a little style, it could look something like this:
In that example, the distinct parts of the table that Fitter cares about are formatted differently to make it easier to talk about those parts.
As you can see, the first row of the table contains its “title”. In
this case, the title is Tutorial.Math, Tutorial
. This identifies
what fixture this table will run. .NET developers should recognize
this as containing a namespace name, class name, and assembly name.
There are ways to make this prettier which we’ll go over later.
In the above example, the cells in the first row have been merged and the font size has been increased to make it stand it. This makes the first row look like a title to us humans, but all Fitter cares about is that the title be located in the first cell of the first row.
The remaining rows in the table indicate the data that the fixture identified by the title row accepts as input or provides as output.
The first column, containing the cells with the gray background,
indicate the names of the inputs and the outputs. As you can see, the
Math
fixture accepts or provides three pieces of data. The names are
A
, B
, and Sum
.
To distinguish between the inputs and outputs, Fitter allows you to
use a ?
as the suffix of a name to mark the outputs. Based on the
above example, you should be able to see that only one row, the row
containing Sum?
, is an output. The other two rows, A
and B
,
specify inputs.
When the Math
fixture runs, it will be given the value 1
for A
and the value 2
for B
. After it’s done, the value the fixture
holds for Sum
will be written to the table so that you could see
what the result was.
After “running” the test document (which we’ll cover below), the result document might look like this:
Notice how the previously empty cell in the second column of the last
row (next to Sum?
) now contains a value. The value it contains is
colored light gray to indicate that it was not typed into the original
document and was placed there by running the test.
You can no doubt tell that the calculated sum is correct, but wouldn’t it be more useful if the test runner could tell you if it was correct without forcing you to open the result document, look at the inputs, do the math in your head, and compare it to the output?
Instead of leaving the output cells empty, test authors can specify what outputs they expect by entering values in those cells. After a fixture runs, output values are either written into empty cells or compared against non-empty cells. If the comparison succeeds, the test passes. Otherwise, the test fails and the cell that contained an expected value that wasn’t equal to the actual value is marked to make it obvious why the test failed.
For example, adding an expected output to the previous test document might look like this:
After running the test, the result document would look like this:
The cell containing the expected value is colored green to indicate the actual output value matched the expected value.
Suppose the test had been modified to purposely fail like this:
Running that test would result in this:
This time, the cell containing the expected value is colored red and the actual value is inserted into it, next to the expected value, to help determine why the test failed.
You can extend this example in many different ways. Any number of inputs can be given to the fixtures that operate on them, producing any number of outputs. In the next section, we’ll see what it take to actually write the fixture run by the previous examples.
Writing Fixtures
Fixtures are the objects that contain the code that actually performs the work your test documents indicate with their tables.
Being objects, we need to define a class to use to construct the
objects. You might have recognized the title of the table in the
examples in the previous section looked like a fully-qualified .NET
type name. The class identified by that name needs to be one that
derives from Fitter.Fixture
or one of its sub-classes.
The most commonly used sub-class of Fitter.Fixture
is
Fitter.OperationFixture
. That’s the type of fixture we assumed
exists in the previous section when writing the document.
Operation fixtures use the input values their given to perform operations. As a result, output values may (and usually are) be available for examining after the operation is performed.
Operation fixtures are simply classes that derive from
Fitter.OperationFixture
. That class, might look something like this:
using Fitter;
namespace Tutorial
{
public class Math : OperationFixture
{
}
}
There’s not a lot going on in this class yet, but it’s a start.
Inputs to an operation fixture are supported by adding public fields to the fixture class. Properties and methods are also supported but fields are usually the simplest.
Adding the A
and B
inputs to the class might look like this:
using Fitter;
namespace Tutorial
{
public class Math : OperationFixture
{
public int A;
public int B;
}
}
Our fixture is also expected to support an output named Sum
. Outputs
are added as public field (or properties or methods) just like inputs.
using Fitter;
namespace Tutorial
{
public class Math : OperationFixture
{
public int A;
public int B;
public int Sum;
}
}
There’s nothing required in the source to indicate which fields are
inputs and which are outputs. This is why the test document uses ?
suffixes on output fields. It is possible to use attributes on the
fields to make it clear which fields are inputs and which are outputs,
but it’s not necessary. Using those attributes will be covered later.
So far, the fixture doesn’t actually do anything. To get the fixture
to execute code, we need to override the Operate
method defined in
its Fitter.OperationFixture
base class.
The Operate
method gets invoked after the input fields are written
to on the object. It’s assumed that inside the Operate
method, all
output fields are assigned their resulting values. Once Operate
is
done, the output fields are read and used to either update the
document or compared with the expected results in the test document.
Knowing this, we could add an Operate
method to our Math
fixture
like this:
namespace Tutorial
{
public class Math : OperationFixture
{
public int A;
public int B;
public int Sum;
protected override void Operate()
{
Sum = DomainModel.Math.Add(A, B);
}
}
}
For this example, it’s assumed that DomainModel.Math.Add
is the
method we’re actually testing. Notice how the A
and B
fields are
used by passing them into the Add
method. The return value of the
Add
method is then assigned to the Sum
field which we think of as
being an output.
Assuming the Add
method works correctly, the results of running the
test should appear as they did in the previous section.
This example is admittedly trivial, but it demonstrates the mechanics
of getting data into and out of fixtures. Fixture authors can use
whatever code necessary inside their Operate
methods to perform
their tests. Databases could be accessed, web services could be
invoked, and domain models could be exercised. The only limit is your
imagination.
Operation fixtures are just one kind of fixture. The other commonly
used fixture type are query fixtures. Those are classes that derive
from Fitter.QueryFixture
. They have a different purpose and are
implemented to be used differently, but learning about them will have
to come later.
Running Tests
TODO: Show screenshots on how to use Happier to run the tests. Include showing what files to put in the folder (Fitter.dll, Happier.dll, the fixture assembly, and the test document). Also show to create a .fitterproj file to configure where tests and assemblies are located.