Introducing xUnit.BDDExtensions
xUnit.BDDExtensions is a little framework build on top of xUnit which enables a BDD style of testing with xUnit.
Besides an emphasis on a “test case class per fixture” organization scheme for tests, AAA (Arrange, Act and Assert) style of writing tests and left to right assertions, this framework also provides Rhino.Mocks integration out of the box. By hiding the mechanics of Rhino.Mocks (record and replay model, mocks vs. stubs) behind the scenes, xUnit.BDDExtensions also helps on writing cleaner, easier to understand and less brittle tests
xUnit.BDDExtensions is based on JP Boodhoos BDD extensions for MBUnit used in his “Nothing but .NET” bootcamps.
Enough of the talking, let’s look at some code
This is actual code of the xUnit.BDDExtensions trunk. The SpecificationTestCommand class serves as a kind of adapter inside xUnit. It enables the test class to have methods that are run before and after the actual test method is executed.*
public class SpecificationTestCommand : BeforeAfterCommand
{
public SpecificationTestCommand(
ITestCommand innerCommand,
MethodInfo testMethod)
: base(innerCommand, testMethod)
{
}
public override MethodResult Execute(object testClass)
{
var specification = testClass as ITestSpecification;
try
{
if (specification == null)
{
throw new InvalidOperationException(
"Instance does not implement ITestSpecification");
}
specification.InitializeSpecification();
return base.Execute(testClass);
}
finally
{
if (specification != null)
{
specification.CleanupSpecification();
}
}
}
}
Notice several things:
- It expects the instance to implement ITestSpecification.
- It calls the InitializeSpecification() method on it
- Passes the execution to the inner command (in base.Execute(obj))
- and finally cleans up the specification by calling CleanupSpecification() on it.
So how can a completely isolated happy path test for this look like?
[Concern(typeof(SpecificationTestCommand))]
public class when_a_specification_test_command_is_executed_on_a_ITestSpecification_implementer :
concern_for_specification_test_command
{
private MethodResult expectedTestResult;
private ITestCommand innerCommand;
private MethodInfo methodInfo;
private MethodResult testResult;
private ITestSpecification testSpecification;
protected override void EstablishContext()
{
innerCommand = Dependency();
testSpecification = Dependency();
methodInfo = GetTestMethodHandle();
expectedTestResult = CreateMethodResult(methodInfo);
innerCommand.WhenToldTo(x => x.Execute(testSpecification))
.Return(expectedTestResult);
}
protected override SpecificationTestCommand CreateSut()
{
return new SpecificationTestCommand(innerCommand, methodInfo);
}
protected override void Because()
{
testResult = Sut.Execute(testSpecification);
}
[Observation]
public void should_ask_the_test_specification_to_initialize()
{
testSpecification.WasToldTo(x => x.InitializeSpecification());
}
[Observation]
public void should_pass_the_test_specification_to_the_inner_test_command()
{
innerCommand.WasToldTo(x => x.Execute(testSpecification));
}
[Observation]
public void should_return_the_test_result_from_the_inner_test_command()
{
testResult.ShouldBeEqualTo(expectedTestResult);
}
[Observation]
public void should_ask_the_test_specification_to_cleanup()
{
testSpecification.WasToldTo(x => x.CleanupSpecification());
}
}
Notice several things:
- In the EstablishContext() method the whole context for the test is created. This includes creating most of the dependencies with Rhino.Mocks through Dependeny() and the necessary configuration of their behavior. This is done in order to quickly get from an interaction based style of testing to a state based style of testing. In AAA terms this method implements the Arrange part.
- The CreateSut() method is executed after EstablishContext() and creates the actual system under test.
- The Because() method implements the actual behavior under test. In AAA terms this is the Act part. In the current example this meant executing the test command with an instance implementing the expected interface.
- Each method marked with the ObservationAttribute contains a single observation which should be fulfilled when the test has been executed. In AAA terms every Observation is an Assert.
That’s basically it. Pretty straight forward imho. If I managed to make you at least a bit curious about this style of testing, you can grab the source with any Subversion client from:
http://xunitbddextensions.googlecode.com/svn/trunk/
Enjoy coding
*
I know xUnit dropped this behavior in favor of the dispose pattern, but having explicit, non technically named methods for that still seems more natural in the context of BDD to me.


9 Responses Leave a comment
1. In my opinion, CleanupSpecification looks the same technically as Dispose. You hide this method behind the base class anyway.
2. The need to explain how EstablishContext, CreateSut and Because correspond to AAA indicates the lack of intent revealing. What about that?
[Concern(typeof(SpecificationTestCommand))]
public class when_a_specification_test_command_is_executed_on_a_ITestSpecification_implementer :
concern_for_specification_test_command
{
…
protected override SpecificationTestCommand Arrange()
{
…
return new SpecificationTestCommand(innerCommand, methodInfo);
}
protected override void Act()
{
testResult = Sut.Execute(testSpecification);
}
[Assertion]
public void should_ask_the_test_specification_to_initialize()
{
testSpecification.WasToldTo(x => x.InitializeSpecification());
}
}
AAA imho is more a clean layout for tests, than a good wording for specifications.
I’m wondering why you’ve chosen for EstablishContext to be called before each test. With such cut-down specifications, and where the specifications just assert (no side affects), why not just have the setup of the fixture and the action on the SUT done once?
Interesting idea. Never thought of that. Implementing that using Because() as the only test method on the fixture class shouldn’t be that difficult, too.
The only problem I currently see is that I’m not able to use the Resharper Test Explorer in meaningful ways anymore (because it would only display the Because() method and not the Observations). The current approach works fine in both TD.NET and Resharper…
By the way: Very interesting blog, you’re on my list now
Hi,
will xUnit.BDDExtensions work with CC.NET and xUnit Build Taks for MSBuild out of the box? My Build log always shows:
Summary
Tests run: 0 Failures: 0, Skipped: 0, Run time: 0s
I’m not a CC.NET user, so I can’t answer that question with certainty.
What I can say for sure is xunit.console.exe runs xUnit.BDDExtensions out of the box. Same applies to Resharper.
I’ll look into the MSBuild task today or tomorrow, in order to detect what might be wrong. Shouldn’t be too hard to fix . . .
Hi Björn,
xUnit.BDDExtensions looks interesting.
I have just tried to get latest and build the source. However, the project is missing a file called DependencyOptions.cs. Actually, the project includes the file, but the file is not in the repos. Please can you add it?
I will cross-post this to the GC issues list too.
Ben
Hey Ben,
fixed it. Sloppy me. Thank you.
Björn
Hi Björn,
just fyi: it runs out of the box with cc.net when configuring cc.net according to http://www.codeplex.com/xunit/Wiki/View.aspx?title=HowToUseCcNet
Alex