FitNesse with .Net

This entry is part 1 of 5 in the series FitNesse

Why another tutorial on installing and using FitNesse with .Net? Well, because I had a lot of trouble installing and using FitNesse with .Net. That may be an issue with the existing tutorials, or, it be an issue with me. Hopefully with these posts I can spare you some of the pain I encountered.

First things first. The source for all things FitNesse is here. There you’ll find lots of tutorials, samples and other useful info. In fact, if you stick at it you’ll get up and running using just the information on that site, but if you’re in any way like me you may find you slip off the happy path a few times. That’s where the tutorial you’re reading comes in.

History
Before you wrap your head around FitNesse, it helps to have an understanding of where it came from. The Framework for Integrated Testing (FIT) was developed by Ward Cunningham. The origins of FIT go back to a project where Cunningham discovered that he could allow users to enter test scenarios into a spreadsheet, and with a bit of coding he could make those test scenarios execute against the project code.

FIT is a publicly distributable version of that idea. Requirements can be written in a standard word document. Specific “examples” or “Test Cases” are created as Tables, and FIT enables us to hook that document up to executable code, run the tests and actually insert the results back into the Word Document.

FitNesse takes this notion a step further and provides a self contained Wiki complete with it’s own web server. Requirements can be entered into the Wiki or edited by anyone with access. A number of styles of tables give a means of demonstrating requirements using Sample Data. Like FIT, the FitNesse wiki can be “run” against our code base, and the results of tests inserted back into the Wiki.

Fit and SLiM
Originally FitNesse used Fit as a means of hooking the tests (in the Wiki) up to executable code. Later the developers of FitNesse set about replacing Fit. The result was SLiM.

Either Fit or SLiM can be used and some would argue that they each have benefits. As I progress through this series of posts I will cover both options.

I’ll begin with SLiM, for no other reason than I found it very easy to get started with when I first approached FitNesse. When we’re comfortable using FitNesse with SLiM we’ll cover some of the same ground using Fit and explore where the two approaches differ.

FitNess in Action
So, enough waffle, what does FitNesse look like in practice? The following screenshot shows a Decision Table, just waiting to be run. A Decision Table is one of a number of different types of tables that FitNesse/SLiM provides as a means of capturing, or to be more accurate, illustrating requirements through tests.



The eagle eyed among you will notice that one of those examples looks like it’s expecting the wrong result. When we run our tests that should give us something worthwhile to look at.

Take a look at the left Nav Bar, most of those buttons do interesting things, but for now we’re just interested in the ‘Test’ button. If we click it, our FitNesse tests spring to life, execute themselves against our code base, fill in the results in our table, and highlight the passes and fails in green and read. Or to say it more simply with a picture…this happens…



Notice that one of the tests turns red. Dividing 100 by 4 doesn’t give 26. This isn’t highlighting a bug in the code, it’s highlighting a bug in the test, which can and does also happen from time to time. The point here is that when our expectations aren’t met, we’re told about it.

What just happened?
How did the table know where to find our code and execute it?
How does it communicate with our code, passing parameters to the code, and getting back results? What’s all the text at the top of the screen about?

These and many more questions will be answered during this series of posts. I hope you’ll stay with me. Next up, we install FitNesse and a few more bits and pieces that we need to make it play nice with .Net languages like C#.

Fit Tables and SLiM Tables
Before I close this post let me return to that issue of Fit vs SLiM once more. A big practical difference between the two is the types of tables we can use to define tests. In the example above we used a Decision Table which is a feature of SLiM. The Equivalent Fit table is known as a ColumnFixture.

We’ll start by working through all the tables that SLiM has to offer, then we’ll work through the equivalent Fit tables.

Installing FitNesse

This entry is part 2 of 5 in the series FitNesse

Downloads
FitNesse – The FitNesse application (wiki) with built-in web server.
FitSharp – Library that allows FitNesse to talk to .Net applications.

Options for Setup
There are a number of different ways to set up FitNesse. How you proceed will depend on the nature of your development process and personal preferences. We’ll look at the simplest possible installation in this post. In future posts we’ll look at the other possibilities.

For the purposes of this post we are going to focus on getting a single instance of FitNesse running on your development machine, so that you can learn how to use it. Later we’ll look at how to create a distinct installation of FitNesse for each Solution, and ultimately we’ll look at more advanced topics such as how to give users the ability to edit and create tests, and how to build FitNesse into a Continuous Integration process.

One installation
This approach involves installing FitNesse in a single known location, e.g. C:\DevTools\FitNesse\ and creating all our tests for various projects in subsections within that one Wiki.

Step 1. Install FitNesse
To install a single FitNesse instance, simply download the FitNesse.jar file using the link at the top of this post, and place in a folder of your choice. I keep mine in C:\DevTools\FitNesse. Don’t worry about this too much, once it’s installed you’ll access FitNesse via your browser and you won’t really care too much about where it’s physically located on your hard drive.

FitNesse is distributed as a JAR file, and on first run, the contents of the file are extracted, creating the entire directory structure that makes up the FitNesse Wiki.

Check that it’s working
With your FitNesse jar file in place, you just need to run it. This can be done from the command line.

C:\DevTools\FitNesse>java -jar fitnesse.jar -p 9092

Assuming you have java installed and on your path this should run. On the first run you should see a message confirming that it is unpacking FitNesse.

When FitNesse is running you can view it using your browser, by visiting


http://localhost:9092

Note the port number is set using the -p switch. I’ve used 9092 here. If you don’t specify a port FitNesse will run on port 80, and you won’t have to specify the port in the url.

Personally I like specifying a port for these kinds of apps. It avoids the problem of everything wanting to run on port 80, and I get used to FitNesse being on 9092.

The command to start FitNesse can be placed in a batch file. I use a file called StartFitNesse.bat which I place in the same folder as the FitNesse .jar file.

FitNesse switches

If you want to get a little clever you can try out some of the optional switches that configure FitNesse. We’ll poke into some of these in later posts.

Usage: java -jar fitnesse.jar [-pdrleoa]
        -p <port number> {80} or {9123 if -c}
        -d <working directory> {.}
        -r <page root directory> {FitNesseRoot}
        -l <log directory> {no logging}
        -e <days> {14} Number of days before page versions expire
        -o omit updates
        -a {user:pwd | user-file-name} enable authentication.
        -i Install only, do not run fitnesse after install.
        -c <command> Run the command (same as restful url) and then exit.

Return status is the number of test pages that failed as a result of the command.

Assuming you’ve done everything right, your browser should load the FitNesse Homepage. Congratulations, you have installed FitNesse. It’s a Wiki application running in it’s own little web server. You can play around, follow the links and see where they take you.

With FitNesse running, the next challenge is to write some tests and execute them against some .Net code. Which sounds like a job for Fitsharp.

Step 2. Install FitSharp

To install FitSharp all you need to to is download the zip file from the page linked at the top of this post. Extract the contents of the zip file you downloaded into a folder of your choice. I keep mine in C:\DevTools\FitSharp. Alternatively you could store it as a sub folder of FitNesse e.g. C:\DevTools\FitNesse\Fitsharp.

Once you’ve extracted FitSharp, you’re done. When the time comes to use it, we’ll tell FitNesse where to find it. We won’t write any .Net code yet, we’ll get to that in the next post. This is just about getting everything set up.

One installation per project
This idea of using one FitNesse installation for perhaps multiple projects is all very well, and we could make it work, but there is an alternative. I’ll thank Jeff Kwak for the inspiration here. His suggests including a FitNesse installation in every solution that needs it. This way each Wiki is specific to one project, and more importantly, the Wiki and all the related libraries automatically fall under source control.

I like this approach a lot. I’m going to adopt it for the rest of the tutorial posts. There’s no point in me writing it up and duplicating Jeff’s efforts, head on over to his site and see what he’s done. I’ll meet you back here on our next post, when we actually get down to business and write some C# code.

Adding FitNesse to your .Net Solution

This entry is part 3 of 5 in the series FitNesse

Sample Code

In the last post I mentioned Jeff Kwak’s blog post on FitNesse, specifically his way of creating a FitNesse wiki within your .Net solution.

The rest of the posts in this series will use a particular arrangement of projects based heavily on Jeff’s approach.

I’m not going to rehash his post, however I did have a few problems getting things to work the way Jeff did, and I also wanted to do some things a little differently. In this post I’ll discuss where my approach differs from his.

I’m providing a very basic Solution with FitNesse included and we’ll walk through creating and passing a first Test. Subsequent posts in this series will build incrementally on this Solution.

A Sample Solution
Download the attached zip file and extract it somewhere on your dev box. Mine is extracted to C:\Projects\FitNesseTutorial.

Open the resulting Solution. It has two Projects, Calculator (our system under test) and Calculator.Specifications (our tests).

Let me apologise right now for going down the well-trodden path of simple calculator examples. My goal with these first few posts is to get you using FitNesse in the simplest possible way. There will be time later to consider more realistic examples of how FitNesse might be used in real projects.

I’m not going to try and find some grand integrated application that illustrates all of the features of FitNesse. I will use simple standalone examples as required to best illustrate what I’m talking about.



Before we start worrying about FitNesse, build the solution. It should build successfully without any changes.

Setting up External Tools
This next step is optional, but I think it’s worth doing. I’d like a way to start and stop the FitNesse server from within Visual Studio, and pop up a browser running the Wiki, also from within the IDE. What I’ve got is not as clean as I’d like, but it’ll do until I find a better way.

Starting FitNesse
The sample Solution includes a batch file that runs FitNesse. The first thing we’re going to do is create an External Tool in Visual Studio to run that batch file.

In the External Tools Dialog, Add a Tool called ‘FitNesseStart’. Set the Command to

$(SolutionDir)Specifications\AcceptanceTests\startfitnesse.bat

set the Initial directory to

$(SolutionDir)Specifications\AcceptanceTests

and check the Use Output window checkbox.

The results should look like this



Opening FitNesse in the Browser
You need only start FitNesse once when you load your project. This starts the FitNesse server running. You seperately navigate to the FitNesse wiki using your browser.

You can start and stop your browser as often as you like, the server will keep running in the background.

We’ll set up another External tool which will load the Wiki on the proper page.

In the External Tools Dialog, Add a Tool called ‘FitNesseBrowser’. Set the Command to

Explorer.exe

set the Arguments to


http://localhost:9092/AcceptanceTests

The results should look like this



Stopping FitNesse
As mentioned above, the FitNesse server will continue happily running away in the background even when you start and stop your browser. Visual Studio External Tools provides a mechanism for shutting down tools, however it doesn’t work for FitNesse.

For now the only solution I’ve found is a separate Tool.

The sample Solution includes another batch file that closes FitNesse.

In the External Tools Dialog, Add a Tool called ‘FitNesseStop’. Set the Command to

$(SolutionDir)Specifications\AcceptanceTests\stopfitnesse.bat

set the Initial directory to

$(SolutionDir)Specifications\AcceptanceTests

The results should look like this



Here’s a curious little thing to note. If you look in the Solution Folder you’ll notice that the Calculator.Specifications project is in a folder called ‘Specifications’, and not ‘Calculator.Specifications’.

This is to make the configuration of the External Tools more generic. It means that when you load a different solution, the external tools will Start and Stop that Solutions own instance of FitNesse, as long as they too are kept in a folder called ‘Specifications’.

The first run
The first time you start FitNesse, the jar file will be extracted to create the Wiki. You saw this in the earlier post. Once everything is extracted the FitNesse server starts running. It all looks like this



First view of the Wiki
With FitNesse running, it’s time to take a look at our Wiki. Select the FitNesseBrowser tool from the Tools menu. What you see should look something like this



Not what you were expecting? Were you thinking you’d see the FitNesse Front Page?

What we’ve done here is make the Tool jump to a specific page in the Wiki, and not going to the regular Front Page. This avoids the need to navigate to our tests every time we start the Wiki.

Since we haven’t created this page yet we’re asked to edit it. So, let’s do that, copy and paste the following code, overwriting the existing contents of the page.

!path bin\Calculator.Specifications.dll

!|import |
|Calculator.Specifications.Fixtures|

This should do simple arithmetic

!|Do Simple Maths |
|A |B |Op|Result?|
|1.0|2.0|+ |3.0 |

Save those changes, but we’re not done with this page yet. Click on the Properties button and change the Page type to ‘Test’. Save the properties and you’ll now see that all important Test Button.



You can click it if you like but it won’t work yet. We have one more piece of setup to do first.

In your Browser, navigate to the URL


http://localhost:9092/root

Edit the ‘root’ page and overwrite the contents with the following

!define TEST_SYSTEM {slim}
!define COMMAND_PATTERN {%m -r fitSharp.Slim.Service.Runner,bin\fitsharp.dll %p}
!define TEST_RUNNER {bin\Runner.exe}

This tells FitNesse to use SLiM which is one of the two protocols that FitNesse can use for linking tests to code. We’re running the implementation of SLiM provided by fitsharp which lets us work with .Net assemblies.

Yes, we could have inserted this bit of configuration at the top of every Test Page, but by putting it in root we can forget about it and stop cluttering up our tests.

Save this page and return to


http://localhost:9092/AcceptanceTests

Now when you click Test, it should work. We have a Test that runs. Admittedly it’s a failing Test, but that’s what we want as a first step.



To get this test to pass we need to look at our code. Our Calculator class throws NotImplementedExceptions rather than implement the actual required logic.

public class Calculator
{
public static decimal Add(decimal a, decimal b)
{
throw new NotImplementedException("Add");
}

public static decimal Subtract(decimal a, decimal b)
{
throw new NotImplementedException("Subtract");
}

public static decimal Multiply(decimal a, decimal b)
{
throw new NotImplementedException("Multiply");
}

public static decimal Divide(decimal a, decimal b)
{
throw new NotImplementedException("Divide");
}
}

Let’s fix that Add method, rebuild and retest.

public static decimal Add(decimal a, decimal b)
{
return a + b;
}



If you’ve made it this far you now have a Solution that has a project under test and a project that has the tests themselves along with all the binaries needed to run FitNesse and use it to test .Net code.

We’ve set up some External Tools in Visual Studio that will make life a little easier.

If you’re a little unsure about how this all hangs together, don’t worry. Over the remaining posts in this series I’ll did a little deeper into the things you can do with FitNesse, and along the way you’ll learn what all the parts of the puzzle are. All posts will use this Solution structure as a basis.

FitNesse Decision Tables

This entry is part 4 of 5 in the series FitNesse

In the last post, we set up a solution to demonstrate the use of FitNesse. The solution contained a Calculator Project, and a Calculator.Specifications project which contains an instance of FitNesse, and an Acceptance Test fixture for the Calculator project.

To show that everything was up and running we created a simple Decision Table test in FitNesse.
In this post we’ll look more closely and Decision Tables.

Decisions Decisions
A FitNesse Decision Table allows us to supply a number of input parameters and receive back a number of output values. A test passes or fails based on whether the expected outputs match the actual outputs.

In our last post we tested a simple calculation using our Calculator project. Let’s look at the Markup for that Table, line by line.

Line 1 defines the name of the table, and by convention this also tells FitNesse the name of the Class it should try to use when executing the test. In this case FitNesse will search our TestFixtures for a class called DoSimpleMath. Alternatively the Fixture name ‘DoSimpleMatch’ could have been used in the table.

The Exclamation mark at the start of Line one is interesting. If the Table name had been written without spaces as described above, FitNesse would have automatically turned the table header into a link.

The Exclamation mark prevents links being automatically created. Strictly speaking in this instance it’s not needed, since FitNesse does not try to create links where spaces exist. It is however good practice to include the exclamation even when not needed.

Line 2 defines the parameters of the test, but the input values we’ll sent to the test, and the output values we get back. In this case A, B and Op are all input parameters. Result is an output value as indicated by the question mark ‘?’.

Line 3 consists of the values to be used with the Test. We supply 1, 2 and + and we state that we expect the value 3 to be the result of adding 1 and 2.

!|Do Simple Maths  |
|A  |B  |Op|Result?|
|1.0|2.0|+ |3.0    |

Naturally, you can create multiple rows to test various scenarios.

!|Do Simple Maths  |
|A  |B  |Op|Result?|
|1.0|2.0|+ |3.0    |
|5.0|5.0|- |0      |
|2.0|3.0|* |6.0    |
|6.0|3.0|+ |2.0    |

Decision Table Fixtures
As mention above the name of the Table dictates the name of the class that FitNesse will attempt to execute the test.

It may be that a table name and the particular input and output columns are ideal for describing features of the system, but are not good names for objects in our domain model.

This is where Fixtures come in. Generally speaking we don’t want to write FitNesse tests that directly access the domain model. Apart from the fact that good names for tests may not make good interfaces for objects, there is also a brittleness from hooking up directly to a Domain Model that could change.

Test Fixtures provide a level of abstraction between FitNesse and our System Under Test. Fixtures are usually simple pass through objects that invoke the expected behaviour and return apropriate results.

We declare the inputs as public fields. There’s no benefit in declaring properties and private fields in a fixture. We want the simplest possible implementation that allows us to get values in and out.

public class DoSimpleMaths
{
    public decimal A;
    public decimal B;
    public string Op;

    public decimal Result()
    {
        if (Op == "+")
            return Calculator.Add(A, B);

        if (Op == "-")
            return Calculator.Subtract(A, B);

        if (Op == "*")
            return Calculator.Multiply(A, B);

        if (Op == "/")
            return Calculator.Divide(A, B);

        throw new NotImplementedException(string.Format("'{0}' is not an implemented Operator", Op));
    }
}

The following fixture implements the Result output as a function. If your Decision Table includes multiple outputs you can implement each as a separate function for each.

public class DoDivisionWithRemainder
{
    public int A;
    public int B;

    public int Result()
    {
        return A / B;
    }

    public int Remainder()
    {
        return A % B;
    }
}

If the work of the fixture can be implemented in a single method, you could consider the following alternative approach. Use public fields for all inputs and outputs, then use a method called Execute to populate all of the outputs.

FitNesse will run the ‘Execute’ method before evaluating the outputs.

public class DoDivisionWithRemainder
{
    public int A;
    public int B;
    public int Result;
    public int Remainder;

    public void Execute()
    {
        Result = A / B;
        Remainder = A % B;
    }
}

The following FitNesse Decision Table will work with either of the previous two Test Fixtures.

!|Do Division With Remainder|
|A  |B |Result? |Remainder? |
|13 |5 |2       |3          |

Beyond the Basics
We can do some interesting things beyond the basic decision tables shown above. For example if we have inputs that will be the same for each row in the table, we can define it once in the header.

!|AddAFixedPercentage        |10     |
|Principal                   |Result?|
|0                           |0.0    |
|10                          |11.0   |
|100                         |110.0  |

In the above example, the value 10 is passed into the constructor of the fixture. It can then be used for each row, without needing a column for it in the table.

public class AddAFixedPercentage
{
    public decimal principal;
    private decimal _percentage;

    public AddAFixedPercentage(decimal <span class="hiddenGrammarError" pre="">percentage)
    {
        _percentage</span> = percentage;
    }

    public decimal result()
    {
        return Calculator.AddPercentage(principal, _percentage);
    }
}

If you want to pass more than one parameter to the Fixture you can do that. Just add them to the first row in the table. Make sure that the constructor has the correct number of parameters of the correct types to accept the values in the table.

!|FixtureName  |10 |ABC |3.14 |

When testing our expected results against the actual results, we also have options. We don’t need to find an exact match in order to pass a test. Sometimes an approximate match, or a range of values may be sufficient. Let’s remind ourselves of the markup for an exact match.

!|Do Simple Maths  |
|A  |B  |Op|Result?|
|6  |2  |/ |3      |

If we use the ~= operator we can match on approximately equal values.

|22|7|/ |~=3.14      |

22/7 is the famous approximation of PI, but in reality it’s not all that close an approximation. In fact it all goes wrong if you look beyond 2 decimal places. The Test above will pass because at two decimal places, 22/7 matches 3.14.

We can also check that a value is Grater Than another.

|10|1|+ |>10      |

Or that a value falls within a given range.

|5|3|* |14<_<16   |

When Decision Tables Work Best
As mentioned above, Decision Tables are very useful when we have a set of clearly defined Inputs and Outputs. It may seem that most problems can be reduced to this sort of scenario, and that’s probably true, however we’ll see other options in subsequent posts for other types of scenarios.

FitNesse Query Tables

This entry is part 5 of 5 in the series FitNesse

Query Tables
In the last post we looked at using Decision Tables with FitNesse. Each row in a decision table can be thought of as a Test Case, with some of the columns representing Inputs and others representing expected Outputs.

Now we move on to something a little more complicated. Query Tables represent a set of results. The Table in its entirety represents a single test (e.g. Get All Employees in IT Department).

The table contains a row for each expected result, and the columns represent the attributes of the objects. Here’s a Query Table in FitNesse that loads a set of Employees.

Query Table

The Markup for this table is about as simple as you’d expect.

|Query:All Employees                             |
|Employee Number|First Name|Last Name|Hire Date  |
|1429           |Bob       |Martin   |10-Oct-1975|
|9924           |Bill      |Mitchell |19-Dec-1966|

The first thing to note is the word Query in the first row. This tells FitNesse that the table should be interpreted as a Query Table.

The name of the table ‘All Employees’ will need to map to a Fixture class called ‘AllEmployees’.

The next row contains the column headers. Our Test Fixture is going to need to mark the returned data with these Column names so that FitNesse knows where it fits in the table.

Query Table Fixtures
Before we look at the specific details of how to pass data to and return data from the Query Table Test Fixture, lets look at the structure of the Test Fixture itself.

At its simplest we need only provide a function called ‘Query’ that returns a list of objects. Since it has no parameters the fixture class doesn’t need any special constructor. The query method will get called and should return the table of data.

We’ll get to the exact details of how the list of objects will represent a table of data. For now you just need to understand that when the Tests are run in FitNesse, the ‘Query’ method will be executed, and it returns a list of something.

public class AllEmployees
{
    public List<Object> query()
    {
        throw new NotImplementedException();
    }
}

We can add a constructor to the class, this will allow our test to feed individual parameters into the Test Fixture. This can be useful if we want to pull back a filtered list of data.

public class SimpleQueryFixture
{
    public SimpleQueryFixture(date HiredBefore)
    {
    }

    public List<Object> query()
    {
        throw new NotImplementedException();
    }
}

Constructor Parameters are specified in the first row of the table.

|Query:All Employees                 |01-Jan-2000|
|Employee Number|First Name|Last Name|Hire Date  |
|1429           |Bob       |Martin   |10-Oct-1975|
|9924           |Bill      |Mitchell |19-Dec-1966|

Of course you can provide multiple parameters to your constructor, just make sure the values in the table match the signature of the Test Fixture constructor.

|Query:Employees By Date City Dept|01-Jan-2000|"IT"|"London" |
|Employee Number             |First Name |Last Name|Hire Date|

We can also optionally implement a ‘Table’ method. If supplied this will be invoked before the ‘Query’ method, and it will receive the existing FitNesse table and it’s contents. This can be useful if we want to provide a table of data, or a partially filled table, and have columns filled in or even overwritten.

public class SimpleQueryFixture
{
    public void table(List<List<String>> table)
    {
        throw new NotImplementedException();
    }

    public List<Object> query()
    {
        throw new NotImplementedException();
    }
}

Table Formats
The eagle eyed among you will notice that the data structure passed to the ‘Table’ method is a List of Lists of Strings, whereas the data structure returned from the ‘Query’ method is a List of Objects.

It’s worth understanding how these tables are represented and the differences between them. We’ll see these data structures used again and again as we look at other types of FitNesse/SLiM tables.

The Output Table – From the ‘Query’ method
We’ll start with the output table since that is by far the most common one you’ll use. It uses a nested List of Lists of Lists type arrangement. Here’s a hard-coded example of how you would return a table of employee details from the query method of your Test Fixture. It illustrates the data structure.

public List<Object> query()
{
    return new
          List<Object>{
            new List<Object>{
              new List<String>{"Employee Number", "1429"},
              new List<String>{"First Name", "Bob"},
              new List<String>{"Last Name", "Martin"},
              new List<String>{"Hire Date", "10-Oct-1974"}
            },
            new List<Object>{
              new List<String>{"Employee Number", "8832"},
              new List<String>{"First Name", "James"},
              new List<String>{"Last Name", "Grenning"},
              new List<String>{"Hire Date", "15-Dec-1979"}
            }
          };
}

From the outside our table looks like a List of Objects. Each item in that list represents a row in the table, and is also represented by a List of Objects. Each cell in the row is represented by a List of Strings. This last bit is where the confusion may lie for the uninitiated.

Instead of representing the Column headers once in a row of their own, the table represents each cell in every row as a Name/Value pair in the form of a List of two strings.

This has it’s advantages, you can pull an individual Row out of the Data structure and have everything you need, the column names and cell values.

It is a little unconventional though, there’s a bit of a mismatch between how you view a table and how it’s actually represented. But, once you know what’s happening it’s fine. This incidentally is where the input and output tables differ. Tables that are sent to the Table method represent the column headers as a row in their own right.

On order to avoid creating boilerplate code from scratch every time we use the Query Table, we can take the tasks of translating from domain objects to this list arrangement and make a generic reusable version. We’ll look at how to do that towards the end of this post.

The Input Table – To the ‘Table’ method
Tables that are sent by FitNesse to the ‘Table’ method are represented by a List>. To put that in english, each row is a list of Strings, and a table is a List of these rows. This is much closer to the way you would imagine a table would be represented.

Using our tables from above as an example, if we have a table that shows Employee Number, First Name, Last Name and Hire Date, we could use code like the following to turn the table into a list of Employee objects.

List<Employee> employees = new List<Employee>();

    foreach(var row in table)
        {
            if(row[0] != "Employee Number")
            {
                employees.Add(
                    new Employee(row[0],
                                 row[1],
                                 row[2],
                                 row[3]));
            }
        }

Pardon the horrible code here, but you get the point. We ignore the first row as it contains the column headers. The four items in each subsequent row represent the cell values from the table, we pass these to the constructor of our domain object.

Translating from Domain Objects To Query Table
It’s unlikely that the data in your application will be represented by a data structure that looks anything like that required by the Query Table.

Assuming we have a list of Employee objects, how are we going to knock it into the shape that will allow our test fixture to pass it pack to FitNesse.

Obviously we could hand code each Test Fixture as the need arises, but that’s a lot of boring repetitive boilerplate code.

Below is an example of using Reflection to convert a list of objects into a list that FitNesse can work with. It makes the assumption that the properties of the object are in CamelCase and by splitting the words in the property name we get the Column name from the table. So, ‘EmployeeNumber’ becomes ‘Employee Number’

class SlimTableAdaptor<T>
{
    public List<Object> FromObjects(List<T> items)
    {
        return CreateTable(items);
    }

    private List<Object> CreateTable(List<T> items)
    {
        return items.Select(CreateRow).Cast<Object>().ToList();
    }

    private List<Object> CreateRow(T item)
    {
        PropertyInfo[] properties = GetProperties();
        return properties.Select(property =>
                         CreateColumn(item, property)).ToList();
    }

    private object CreateColumn(Object item, PropertyInfo property)
    {
        string propertyName = SplitCamelCase(property.Name);
        string value = GetPropertyValue(item, property.Name);
        return new List<string> { propertyName, value };
    }

    private PropertyInfo[] GetProperties()
    {
        return typeof(T).GetProperties();
    }

    private string GetPropertyValue(object source, string property)
    {
        object value = source.GetType()
                             .GetProperty(property)
                             .GetValue(source, null);

        string valueAsString = value.ToString();

        if (value.GetType().Name == "DateTime")
            valueAsString = FixDate(value);

        return valueAsString;
    }

    private string SplitCamelCase(string value)
    {
        return System.Text.RegularExpressions.Regex
            .Replace(value, "([A-Z])", " $1").Trim();
    }

    private string FixDate(Object value)
    {
        DateTime date = (DateTime) value;

        if (date.TimeOfDay.Ticks == 0)
            return date.ToString("dd-MMM-yyyy");

        return value.ToString();
    }
}

With a helper class like this written, it becomes a trivial matter to create a Test Fixture class. Here’s what our Query method might look like.

public List<Object> query()
{
    var employees = EmployeeRepository.GetAll();

    var helper = new SlimTableAdaptor<Employee>();
    return helper.FromObjects(employees);
}

This is a simplified version of the helper class. You might like to add some additional features such as

  • Ability to specifically map property names to column names
  • Ability to return a subset of properties rather than all of them
  • Ability to translate from Input Table to list of Objects
  • Ability to translate other sources of Data (e.g. Data Tables, XML etc.)

Subset Tables
There are two specialised types of Query Table that you can use, Subset Query Tables, and Ordered Query Tables. The names pretty much leave nothing to the imagination here.

When we use a Subset Query Table our tests pass as long as the rows we specify exist in the results returned from the Fixture.

When we use an Ordered Query Table our tests pass as long as the rows we specify match exactly the rows returned from the Fixture, including the ordering of the rows.

Subset and Ordered Query Tables work exactly like regular Query Tables, the data is returned in the same format, we can specify constructor parameters and an input table in exactly the same way.

Here’s a Subset Query Table

|Subset Query:All Employees                      |
|Employee Number|First Name|Last Name|Hire Date  |
|1429           |Bob       |Martin   |10-Oct-1975|

As long as one of the rows returned from the AllEmployees test fixture matches the row in the table, the test will pass.

Here’s an Ordered Query Table

|Ordered Query:All Employees                     |
|Employee Number|First Name|Last Name|Hire Date  |
|1429           |Bob       |Martin   |10-Oct-1975|
|9924           |Bill      |Mitchell |19-Dec-1966|

The rows returned from the AllEmployees test fixture must match those in the table exactly, including matching the order in which the rows are listed.

Switch to our mobile site