Navigate / search

FitNesse with .Net – Decision Tables

This entry is part 3 of 5 in the series FitNesse

In the last post, we created a very simple FitNesse test to show that everything is set up correctly, and we can test .Net code.

That example used a Decision Table which is not only one of the easiest tables to use, but also a surprisingly versatile and useful table. In this post we’ll look at the Decision Table in more detail.

Decisions Decisions
Decision tables consist of columns that are either inputs to a test or expected results from the test. Each row in the table represents a separate test case. In the last post we tested a simple calculation using a simple Calculator C# 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 for a class called Calculation.

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 ‘?’.

Lines 3 and consist 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. We have a separate test case which tests that adding 1 and -1 results in 0.

|Calculation   |
|A|B|Op|Result?|
|1|2|+ |3      |
|1|1|- |0      |

Decision Table Fixtures
As mentioned 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.

Aha! Unit Tests – DDDBelfast

Sample Code

On Saturday I spoke at DDDBelfast. My Session was on Unit Testing. After the Test Driven Development session in Bristol, it occured to me that a more fundamental problem was Unit Testing itself. Whether the Tests are Written before or after the code is a non-issue if developers aren’t writing automated Unit Tests at all.

Here are the slides for the session.



The sample Code includes the Wizard project which includes a suite of Unit Tests, you’ll need to install MOQ and NUnit to run all the tests.

Also included are the three examples of injecting mocks, and the example of how traditional NTier code can violate the Single Responsibility Principal, and how to avoid that problem

Any questions about any of this, feel free to get in touch or leave a comment.