Extending xUnit with a Custom ObservationAttribute for BDD Style Testing

An entry about bdd Publication date 19. September 2008 20:32

A couple of weeks ago, I wrote about how I’ve been experimenting a bit with the way I structure my tests in light of moving towards Behavior Driven Development. One of the things I did, was to create a base class for all my tests, and use it to separate the context of the test from the actual testing, so that it could be reused.

In order to make this work with xUnit, my base class was forced to make virtual calls within its constructor – which you really don’t want to do. Alas, xUnit doesn’t have any setup and teardown functionality apart from using the constructor and implementing IDisposable, so there’s wasn’t really any other way for me to get this working. Or was there…?

Extending xUnit

Of course there was! Like any good framework, xUnit can be extended. For one thing, we can create our own test method attribute to manage how any test decorated with it should be executed:

/// <summary>
/// Identifies a method as an observation which asserts the specification
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class ObservationAttribute : FactAttribute
{
    protected override IEnumerable<ITestCommand> EnumerateTestCommands(MethodInfo method)
    {
        foreach(ITestCommand command in base.EnumerateTestCommands(method))
        {
            yield return new ObservationCommand(command, method);
        }
    }
}

In xUnit, each test run is programmatically represented by an instance of something that implements the ITestMethod ITestCommand interface, and through the EnumerateTestCommands method the Fact attribute is allowed to return any number of such instances – which essentially means that any xUnit test method may be executed multiple times per test (something that the Theory stuff in the xUnit Extensions take advantage of – more on that some other time).

My custom Observation attribute simply passes the original test command(s) on, after wrapping it in an ObservationCommand. By using the decorator pattern, we can easily implement this so that it calls the InitializeContext and Observe methods on our Specification fixtures before letting the actual command execute the test:

public class ObservationCommand : ITestCommand
{
    private readonly ITestCommand _innerCommand;
 
    public ObservationCommand(ITestCommand innerCommand)
    {
        _innerCommand = innerCommand;
    }
 
    public MethodResult Execute(object testClass)
    {
        // for specifications, we initialize the context and 
        // perform the observation before executing the test method
        if(testClass is Specification)
        {
            Specification specification = (Specification)testClass;
 
            specification.InitializeContext();
            specification.Observe();
        }
 
        return _innerCommand.Execute(testClass);
    }
 
    public string Name
    {
        get { return _innerCommand.Name; }
    }
 
    public bool ShouldCreateInstance
    {
        get { return _innerCommand.ShouldCreateInstance; }
    }
}

With this in place, our Specification class now looks like this:

/// <summary>
/// The base specification class
/// </summary>
public abstract class Specification
{
    /// <summary>
    /// Initializes the context for the specification
    /// </summary>
    protected internal abstract void InitializeContext();
 
    /// <summary>
    /// Performs the action to observe the outcome of to validate the specification.
    /// </summary>
    protected internal abstract void Observe();
}

Let’s Recap: An Example..

Okay, so I showed an example last time – but lets do another one with the new naming and everything. I create an (abstract) context like this:

public abstract class Behaves_like_repository_with_no_pages : Specification
{
    protected InMemoryDataContext _context = new InMemoryDataContext();
    protected InMemoryRepository<IPage> _repository;
  
    protected override void InitializeContext()
    {
        _repository = _context.GetRepository<IPage>() as InMemoryRepository<IPage>;
    }
}

With this, I can then write specifications that requires such a context in order to be tested:

public class When_creating_a_new_page : Behaves_like_repository_with_no_pages
{
    private IPage _page;
 
    protected override void Observe()
    {
        ICreatePages factory = new PageFactory(_repository);
        _page = factory.Create("Title");
    }
 
    [Observation]
    public void A_new_page_instance_is_created()
    {
        Assert.NotNull(_page);
    }
 
    [Observation]
    public void The_instanace_is_inserted_into_the_repository()
    {
        Assert.Equal(1, _repository.Find().Where(p => p == _page).Count());
    }
 
    /** snip **/
}

I’m getting more and more comfortable with this way of structuring my tests - but be warned that it is a very fine line to walk here before taking the DRY principle too far by pushing too much stuff down into the base class, which will make it very difficult to come back to a test later, or for someone else to read it as an example of how something works.

My view at the moment is that only the setup of the context belongs in the base class; the code that creates the environment in which we want to run the test. In the above example for instance, our context is the existence of a page repository. This is a prerequisite for creating a new page, and not really of interest for the test itself – it is just something that must be there in order for us to be able to test creating a page. Normally, most of the stuff that sets up the context will be code that mocks any dependencies the SUT has.

By separating this from the test class, I think the readability of the test becomes much improved. If you look at it, you can instantly recognise that in order to create a new page, you need to obtain an instance of the ICreatePages interface and call its Create method. Then, by looking at the observations, you can identify what the end result (side effects) will be – that a new page instance is returned, and that it is inserted into the repository.

Where Do We Go From Here?

What I’m doing here, is just enabling a BDD-like way of writing tests within an existing unit testing framework. There are quite a few people out there who are building tailored test frameworks for BDD however – some notable ones for .NET are NBehave and MSpec, plus the very interesting Specter, which takes advantage of Boo’s meta-programmability to make the syntax read more like proper specifications.

For my part, I’m just waiting for better integration with the IDE and tools for these frameworks, before taking the plunge and ditching my current “hybrid” approach. One thing is for sure – I’m really digging the mindset of BDD (which obviously goes far beyond just writing tests, but that’s a post for another day).

Currently rated 3.0 by 2 people

  • Currently 3/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Comments

Powered by BlogEngine.NET 1.4.5.0

Welcome!

My name is Fredrik Kalseth, and this is my blog - thanks for visiting! I am fortunate enough to work with what I love for a living, and this blog is essentially the biproduct of that.

I work as a senior consultant for Capgemini, and am also an active participant in the Norwegian .NET community, as an avid attendee but also as a speaker (most recently at NNUG and MSDN Live).

As a developer, I have a wide circle of interest. My primary passion is for agile, test-driven development, with focus on best practices and clean code. That said, I also love to work on the frontend, especially with web development.

On Twitter? My handle is fkalseth. On LinkedIn? I`m there too.

Disclaimer

This is a personal blog; any opinions expressed here are my own and do not necessarily reflect those of my employer. All content herein is my own original creation, and as such is protected by copyright law. Unless otherwise stated, all source code posted on this blog is freely usable under the Microsoft Permissive License.

What Readers Talk About

Comment RSS