Tests are rarely as simple as the one described in Chapter 4, Writing basic tests. Generally, they involve several steps and verifications. FitNesse allows us to write multi-step test scripts as easily as simple verifications. In this chapter we develop the next story, and learn how to write test scripts and pass values between tables. Our task for this chapter is to implement the second user story:
Register player
As an operator, I want players to register and open accounts before purchasing tickets, so that I have their details for marketing purposes and to prevent fraud.
Again, we speak to our business analysts about what to test and how to verify that the story has been implemented correctly. The first thing they say is that, upon successful registration, personal details should be stored correctly in the system and the player should be able to log in with their registered username and password. Also, the balance for new accounts in the system should always be zero.
![]() | Focus on fixture code |
|---|---|
To keep things simple, we will focus on test fixtures and test-specific code in this and the following chapters. Business classes will be discussed just enough to support the story. You can see the implementations of these classes in Appendix D, Source code. You can also download them from http://gojko.net/fitnesse. |
To implement the story, we need to allow players to register and
log in. Each player will have a unique numeric ID, which should be returned from the
registration method (let's call it
RegisterPlayer) if the operation is successful.
Let's create a data-transfer interface where we will store
all the personal details of the player required for the registration — we will call it
IPlayerRegistrationInfo. The LogIn method
should return the corresponding
player ID, if the username and password are correct. If not, an
exception is thrown. We also need a way to retrieve player details to verify
that they are stored correctly.
So, let's create a PlayerManager
class,
responsible for managing players in our system. To begin with, we
give it this API:
30 int RegisterPlayer(IPlayerRegistrationInfo p); 31 IPlayerInfo GetPlayer(int id); 32 IPlayerInfo GetPlayer(String username); 33 int LogIn(String username, String password);
The test that we need to write for this chapter actually involves a few stages:
Register a new player.
Check that user details were stored correctly and that the balance on the new account is 0.
Try to log in with the username and password provided during registration.
Although everything could be described by one (huge) table, the resulting test page would be completely unreadable, which defeats the whole point of using FitNesse. We can put more than one table on a single page and they will be executed in sequence. This allows us to create a test script that describes the steps with small and focused tables.
Once we divide the test into several tables, we have to handle issues associated with
their interdependence. The first one is that the
tables must work with the same
PlayerManager
instance. A typical solution for sharing this kind of information between
tables is to use a separate fixture to set up the test environment.
This setup fixture
stores the contextual information into static properties. (There is also a standard fixture class
called SetUpFixture, which will be
explained in Use
SetUpFixture to prepare the stage for tests
. In this case,
we are talking about generic setup fixtures that prepare the stage
for other fixtures, not necessarily of any particular class).
Let's call this class SetUpTestEnvironment.
Tristan/test/PlayerRegistration.cs
6 public class SetUpTestEnvironment : Fixture
7 {
8 internal static IPlayerManager playerManager;
9 public SetUpTestEnvironment()
10 {
11 playerManager = new PlayerManager();
12 }
13 }
This class would typically be responsible for initialising service objects,
connecting to the database, and anything else our test environment requires
to run correctly. In the example we extend fit.Fixture
directly, because no parameters are being passed to the class.
When you need to pass connection strings or other setup information,
you can use any other fixture class. ColumnFixture
is a good candidate.
Static values and singletons[14] are fine for storing resources that we can anticipate while writing the FIT integration class, like database connections and service objects. However, tables in the same script often have to share dynamic information. By dynamic, I mean values created on the fly in the tests. For example, we might write a table that registers a new player, and another one that verifies that the player data was stored correctly. The second table needs to know the ID of the player created by the first table. If we were to use a static variable for this, the two test tables would be coupled quite strongly. As soon as we need to perform checks on two players, we would have to modify the registration test class code and add another static variable, making the verification class even more complex, as it would need a switch to indicate which variable to use as the ID.
A better solution for passing dynamic information between
tables is to use FitNesse symbols.
Symbols are global variables that can be accessed
using a simple syntax.
To store a value of an output column
into a symbol named player, write
>>player
into the cell. To read a symbol value, and store it into an input
column, use <<player.
Think of << and >> as arrows pointing the way.
![]() | What is the scope of a symbol? |
|---|---|
Symbols are stored in a static collection inside the
|
![]() | Stuff to remember |
|---|---|
|
[14] A design pattern that restricts classes to only one instance, see http://en.wikipedia.org/wiki/Singleton_pattern

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


