In a recent post, I showed you how to create a basic Fitnesse ColumnFixture test using C#. However, ColumnFixtures are but one test table style available in Fitnesse. I was asked in the comments of my last post to do a tutorial around another style – the ActionFixture.
What is an ActionFixture? Let’s say you have a test which is a sequential series of steps. For example, suppose we have a simple calculator. Users can add numbers together to get the sum, and clear the display. So we may want to test that when a user presses 2, hits add, presses 4, and hits equals, the display shows the number 6. ActionFixtures are made for this kind of thing. However, actually getting them to work in C# can be a little tricky, but nothing that one can’t overcome.
We’ll start with our Calculator
class. We have add, clear and display methods, in addition to methods for each digit. To sum, we just split on the plus sign, and add up the numbers:
using System;
using System.Text;
namespace FitnesseTutorial
{
public class Calculator
{
private StringBuilder displayLine;
public Calculator()
{
displayLine = new StringBuilder();
}
public void add()
{
displayLine.Append("+");
}
public void clear()
{
displayLine = new System.Text.StringBuilder();
}
public string display()
{
return displayLine.ToString();
}
public void sum()
{
String line = displayLine.ToString();
String[] numbers = line.Split('+');
int total = 0;
foreach(String number in numbers)
{
int num = int.Parse(number);
total += num;
}
displayLine = new StringBuilder();
displayLine.Append(total.ToString());
}
public void one()
{
displayLine.Append("1");
}
//same as one for the other digits
}
}
With that code in mind, our Fitnesse test seems pretty easy. We want a way to enter numbers, press sum, and check the display. Something like:
|check|display|blank|
|press|one|
|check|display|1|
|press|add|
|press|2|
|press|sum|
|check|display|3|
would be fantastic. So how do we get there? Looking again at the ActionFixture, it looks like we should create a fitnesse test like:
!define COMMAND_PATTERN {%m %p}
!define TEST_RUNNER {dotnet\FitServer.exe}
!define PATH_SEPARATOR {;}
!path C:\FitnesseTutorial\bin\Debug\FitnesseTutorial.dll
|Action Fixture|
|start|!-FitnesseTutorial.CalculatorTest-!|
|check|display|blank|
|press|one|
|check|display|1|
|press|add|
|press|two|
|press|sum|
|check|display|3|
To create my test fixture, I’m going to have to duplicate a lot of the methods from my calculator so I can delegate them to it. (This was one of the few instances where I wished for multiple inheritence so I could just subclass ActionFixture and Calculator, but I digress).
using System;
using fit;
using fitnesse;
namespace FitnesseTutorial
{
public class CalculatorTest : fit.ActionFixture
{
private Calculator calculator;
public CalculatorTest() : base()
{
calculator = new Calculator();
}
public void add()
{
calculator.add();
}
public void clear()
{
calculator.clear();
}
public string display()
{
calculator.display();
}
public string sum()
{
calculator.sum();
}
public void one()
{
calculator.one();
}
//more number delegation
}
}
So, we’ve got our Fitnesse test, we’ve got our fixture, and we’ve got our target class. We should now be ready to run the test and see a nice green bar:
Ah, forgot the namespace. ActionFixture
is in the fit
namespace, so I’ll just add that to the test by saying:
|!-fit.ActionFixture-!|
(The !- -! around the name escapes it from being treated as a Wiki word. Without this, Fitnesse will see it as “fit.ActionFixture?” because that’s what the wiki makes it to be if you don’t have a page defined for that word)
Ok, with that hiccup, let’s get that green bar:
Huh. Well, I can’t even add the image, because it’s too big. But, every method after start
failed with:
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.NullReferenceException: Object reference not set to an instance of an object.
at fit.CellOperation.GetAccessor(Fixture fixture, String memberName)
at fit.CellOperation.Check(Fixture fixture, String memberName, Parse cell)
at fit.ActionFixture.Check()
--- End of inner exception stack trace ---
at System.Reflection.RuntimeMethodInfo.InternalInvoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean isBinderDefault, Assembly caller, Boolean verifyAccess)
at System.Reflection.RuntimeMethodInfo.InternalInvoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean verifyAccess)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.MethodInfo.Invoke(Object obj, Object[] parameters)
at fit.MethodAccessor.Set(Fixture fixture, Object value)
at fit.ActionFixture.DoCel
ls(Parse cells)
After some digging, and discussion on the Fitnesse mailing list, I discovered that there seems to be a bug in ActionFixture for C#, so we need so subclass it and set a variable so the parents know what the object we are working on is. In other words, change your Fitnesse test to look like:
|!-FitnesseTutorial.CalculatorTest-!|
|start|!-FitnesseTutorial.CalculatorTest-!|
|check|display|blank|
|press|one|
|check|display|1|
|press|add|
|press|two|
|press|sum|
|check|display|3|
meaning your Fitnesse test will use your fixture instead of the base ActionFixture. Then, modify the constructor for your fixture to look like:
public CalculatorTest() : base()
{
targetObject = this;
calculator = new Calculator();
}
the change is bolded above. targetObject
is a protected field from ActionFixture that isn’t set for some reason, even though it is needed. So, with that change, we rerun the test:
And we have a green test!
So, lessons learned. Writing an ActionFixture
is pretty straightforward, as long as you remember to have the Fitnesse test use your custom fixture instead of the base ActionFixture
.
Code:
Hi Cory,
I have similar kind of problems with C#, fit-framework and ActionFixture. Every method fails with message:
“System.NullReferenceException: Object reference not set to an instance of an object.
at fit.ActionFixture.doCells(Parse cells)” Column fixtures are working fine.
I am using fit DotNet version 1.1 (downloaded from http://sourceforge.net/projects/fit/) and Visual Studio 2005 edition. It looks like DotNEt version 1.1 of fit does not have protected field “targetObject”.
Do you have any hints how to fix that problem?
Thanks,
Antti
Ah yes, good point. The version of Fitnesse I ran was from http://www.fitnesse.org/FitNesse.SourceCode – I had the 20060209 version.
Try that and let me know if it helps.
Cory
Hi Cory,
I started to use Fitness instead of fit. Your workaround seems to work fine with Fitness.
Anyhow, I cannot use fit-fileRunner.exe with the version 20060209. It looks like fit and Fitnesse frameworks are incompatible.
Thanks. You have some very helpful tutorials. Any more coming?
Hi Cory,
Your fitnesse tutorials saved me from a whole lot of work. Thanks alot.
I just need a tiny help. My PM is bugging me for an actual file that captures the results from the test, and i seriously have no idea where to find it. Could you give an insight on where I can find it or what I can do?