In Chapter 2, Installing FitNesse, you had a brief introduction to FitNesse tests. Now is the time to take a deeper look at the bridge between test and domain code. In this chapter you also learn why FitNesse tests are more understandable than unit tests. Our task for this chapter is to implement the first user story:
Calculate expected winnings
As an operator, I want the system to display expected winnings, so that players will be enticed to buy tickets.
To implement the story, we will write a WinningsCalculator class,
which will be responsible for calculating expected winnings for a given
draw pool value.
Instead of writing the WinningsCalculator immediately, we take
a step back. Remember Guiding the development? The first step of
implementation is to decide how we are going to verify that the result behaves correctly.
Just to make sure you did not
jump over this idea, repeat out loud: The first step of
implementation is to decide how we are going to test the result! And we need to
agree on that with someone from the business side, to be sure that the result is really
what they want.
So, after a short discussion with
our business analysts, we decide
that the best way to test the winnings calculator is to take the
results from the last month's draw (Figure 4.1, “Last month's results”), put the pool size into the
calculator and check whether the numbers match.
Test-driven development by the book advocates writing the test before
we actually write the production code whenever possible. The test then serves as
a target for development.
So, let's write just enough of the
WinningsCalculator
class
to be able to compile the test. We need to test two things: calculating the pool percentage, and calculating
the prize pool.
Figure 4.1. Last month's results
| Winning combination | Pool allocation | Value |
|---|---|---|
|
Total pool |
100% |
$4,000,000 |
|
Payout pool (PDP) |
50% |
$2,000,000 |
|
6 out of 6 |
68% of the PDP |
$1,360,000 |
|
5 out of 6 |
10% of the PDP |
$200,000 |
|
4 out of 6 |
10% of the PDP |
$200,000 |
|
3 out of 6 |
12% of the PDP |
$240,000 |
![]() | Write the FitNesse page before you write code |
|---|---|
In order to explain the syntax of various FitNesse tables in this book, we typically write the fixture code first and then look at how that code maps to FitNesse tables. But once you learn what those fixtures can do for you, it is actually much better to create the FitNesse page first and let that lead you while writing the fixture class and changing the underlying business interfaces. Writing the FitNesse test page first will allow you to make sure that programmers and customers have the same understanding of the problem. Keep working on the FitNesse pages until both you and the customer think that you have sufficient examples to start programming. |
Tristan/src/InitialWinningsCalculator.cs
1 namespace Tristan
2 {
3 public class WinningsCalculator
4 {
5 public int GetPoolPercentage(int combination)
6 {
7 throw new Exception("Not implemented");
8 }
9 public decimal GetPrizePool(int combination, decimal payoutPool)
10 {
11 throw new Exception("Not implemented");
12 }
13 }
14 }
The A quick test touched upon the subject
of the thin integration layer built on top of business code that FIT requires.
This is the integration class fit.Fixture, which provides hooks to
relevant properties and methods of business objects and tells FIT
how to run the test. Although
fit.Fixture
is always the base class
for all integration classes, it does not specify
how to run the test. Instead, we typically extend a subclass of
Fixture for our tests. In the rest of the book,
we'll call such subclasses fixtures. There are many ready-made
fixtures in the basic FIT package (fit.dll) and
the popular extension FitLibrary[13]
(fitlibrary.dll). Also, you can
develop your own fixtures to extend the functionality of FitNesse (see
Implement domain-specific tests using custom fixtures) so
there are quite a few candidates to choose from.
Which fixture class should we use in this case?
To test the WinningsCalculator class,
we need to check that the allocated percentage of the
payout pool and prize value are correct
for all winning combinations (and a given value of the payout pool).
If the test is a calculation, described in the form of
“check that results are
correct for given inputs” and there are a
few known inputs to try out, we should
use ColumnFixture as the base for the integration
class.
The fixture class should allow us to define the total value of the payout pool and the winning combinations, and check allocated percentages and prize pool values. So let's create two properties for the inputs and two methods to calculate results:
1 namespace Tristan.Test
2 {
3 public class PayoutTable:fit.ColumnFixture
4 {
5 private WinningsCalculator wc=new WinningsCalculator();
6 public int winningCombination;
7 public decimal payoutPool;
8 public int PoolPercentage()
9 {
10 return wc.GetPoolPercentage(winningCombination);
11 }
12 public decimal PrizePool()
13 {
14 return wc.GetPrizePool(winningCombination, payoutPool);
15 }
16 }
17 }
Now we write the test page. Add a link to PrizeCalculation from the home page (as explained in Don't forget the test), then click this link and create a new page. Add the three setup lines, described in How FitNesse connects to .NET classes, defining the test runner and location of the project DLL. Make sure to enter the correct path to DLLs on your system; it may differ from the one in this book.
The table for ColumnFixture
tests has at least three rows: the first row specifies the fixture class name,
the second names input and output methods and properties, and the following rows
specify test data and expected results. In this case,
payoutPool and
winningCombination
are inputs, and methods PoolPercentage and
PrizePool calculate output values. (Fields and properties with a getter can also be used
for test outputs.) To differentiate between
inputs and outputs, PoolPercentage and
PrizePool end with a question
mark in the second row. Parentheses () can also be used to
specify outputs,
but to keep things consistent when properties are used for outputs, and
to make tables easier to read, I recommend that you just use the question mark.
Rows after the second row just contain
last month's draw results. Here is the table that you should copy into the page:
4 !|Tristan.Test.PayoutTable| 5 |payoutPool|winningCombination|PoolPercentage?|PrizePool?| 6 |2000000|6|68|1360000| 7 |2000000|5|10|200000| 8 |2000000|4|10|200000| 9 |2000000|3|12|240000|
Save the page, then tell FitNesse that this is a test page (using page properties).
You can run the test by clicking Test. Because we
have not yet written the whole WinningsCalculator class, the test
fails. Now that we have a clear understanding of what the class should do, let's
write it to satisfy the test.
Tristan/src/WinningsCalculator.cs
1 namespace Tristan
2 {
3 public class WinningsCalculator
4 {
5 public int GetPoolPercentage(int combination)
6 {
7 switch(combination) {
8 case 6: return 68;
9 case 5: return 10;
10 case 4: return 10;
11 case 3: return 12;
12 default: return 0;
13 }
14 }
15 public decimal GetPrizePool(int combination, decimal payoutPool)
16 {
17 return payoutPool * GetPoolPercentage(combination) / 100;
18 }
19 }
20 }
Recompile the project, run the test again, and it passes (Figure 4.2, “Winnings Calculator works!”).
![]() | Stuff to remember |
|---|---|
|
[13] FitLibrary is a set of extensions developed by Rick Mugridge, now considered part of the standard set of fixtures, although it is technically a separate library. The FitSharp package already contains the FitLibrary, so you do not have to download it separately. We use FitLibrary fixtures in Chapter 6, Writing efficient test scripts and Chapter 8, Coordinating fixtures.

![[Important]](../images/resources/important.png)
![[Tip]](../images/resources/tip.png)

![[Note]](../images/resources/note.png)


