BuilderFramework – a framework for committing and rolling back test setup

In a recent piece of work the need has come up again to write some builder code for use with tests.  I feel passionately that you should take as much care with your test code as you do with your main software code that goes out of the door.  The reason for this is that your tests are your specification.  They prove the software does what it says it is going to do.  Having well written, clean and repeatable tests is vital.  Tests that are hard to maintain and brittle get ignored when they aren’t working.  Unfortunately you hear phrases like “Oh don’t worry about that test it’s always broken” all too often.

Part of the reason that a lot of tests I see out in the real world aren’t repeatable is that they rely on 3rd party systems that they can’t control very easily.  The biggest one of these is a database.  I’ve seen acceptance tests relying on a certain set of products having certain attributes in a database.  It’s not hard to see why this isn’t a great idea.  As products change in the database the tests start breaking.

To fix this problem a nice approach is to use the builder pattern to build setup your data in a certain way, run your test and then roll the data back to how it was before.  This is something that I have written various times so I’ve decided to start an open source project on github.  The product will provide the boiler plate code so you can just concentrate on writing the steps.

The project will have an interface that you have to implement that looks like this:

public interface IBuildStep
{
    void Commit();
    void Rollback();
}

It doesn’t get any more straight forward than that. Once you have written a build step you will be able to add it to the main builder in 2 ways. The first way is you can just pass in an instance:

var builder = new Builder()
                    .With(new MyStep());

The second way is that you can provide a type that can build your step. This allows you to write a builder that can build up a class using a fluent interface and just plug it in. For example if I had a builder:

public class MyStepBuilder
{
    private MyStep _myStep;

    public MyStepBuilder()
    {
        _myStep = new MyStep();
    }
    
    public MyStepBuilder WithValue(int value);
    {
       _myStep.Value = value;
    }
    // more methods here to setp all of the properties on _myStep
    // ...

    public MyStep Build()
    {
       return _myStep;
    }
}

Then you would be able to plug that builder into the main builder giving you a fluent interface:

    var builder = new Builder()
                        .With<MyStepBuilder>(s => s.WithValue(3)
                                                   .Build());

Either way once you have an instance of the builder you can then commit your steps by calling commit and then roll them back by calling rollback.

Keep an eye on the github project to see how this developed. If you like the idea or have any thoughts about how it can be improved please give me a shout.

Advertisements

Strongly Typed ScenarioContext in Specflow

I write a lot of BDD tests day to day using SpecFlow.  I really love SpecFlow as a tool, as it allows you to write clear acceptance tests that are driven by business criteria and written in a language that the business can understand.

One of the things I dislike about SpecFlow however is the way the ScenarioContext is implemented.  The ScenarioContext is a dictionary that is passed to each step in your test by the SpecFlow framework.  It is a place where you can store test data between steps.  The trouble is the signature to get and set an item is as follows:

// Set a value
ScenarioContext.Current.Add(string key, object value);

// Get a value
var value = ScenarioContext[string key];

The trouble with this is that when people are not disciplined you end up with magic strings everywhere, scattered throughout all of your step definition files.  You also lose your compiler as a safety guard.  For example:


[Binding]
public class Steps
{
    [Given("I have done something")]
    public GivenIHaveDoneSomething()
    {
        ScenarioContext.Add("customerId", 1234);
    }

    // then further down
    [When("I do this action")]
    public WhenIDoThisAction()
    {
        // This line will throw a NullReferenceException
        var customerId = (int)ScenarioContext.Current["wrongKey"];

    }
}

The two problems that I mention are highlighted above. Firstly you have to remember the magic string that you used in the ScenarioContext. You also have to know the type to cast it. The above code will compile but will throw an error at runtime. Runtime errors are much harder to catch than compile time errors. Where possible it’s best to make the compiler work for you.

To aid with that I have written a nuget package called StronglyTypedContext. This nuget package provides you with the ability to have strongly typed scenario contexts alleviating both of the problems above.

Example using StronglyTypedContext nuget package:


public interface IContext
{
    public int CustomerId { get; set; }
}

[Binding]
public class Steps : BindingBase
{

    [ScenarioContext]
    public virtual IContext Context { get; set; ]

    [Given("I have done something")]
    public GivenIHaveDoneSomething()
    {
        Context.CustomerId = 1234;
    }

    // then further down
    [When("I do this action")]
    public WhenIDoThisAction()
    {
        var customerId = Context.CustomerId;

    }
}

Look how much cleaner the code above is. No casting is necessary and we still have access to our ScenarioContext variables throughout the test. The heavy lifting for this is done using a constructor the base class (BindingBase) which uses Castle Dynamic Proxy at runtime. Note how in the code above nowhere do we actually implement the IContext interface?

So how does this work?

1) The abstract class BindingBase has a constructor that looks for public virtual properties marked with ScenarioContextAttribute

2) Castle Dynamic Proxy is then used to generate a proxy implementation for each property and sets each property to the created proxy. The proxy is created with a custom interceptor set.

3) The custom interceptor is invoked when you call get or set on the public virtual property. For example in the code snippet above when you call Context.CustomerId you are actually invoking CustomerId on the proxy which gives the interceptor a chance to get involved.

4)  The interceptor then checks to see if the property is being get or set. It then either retrieves or stores the value in the SpecFlow Scenario context based on a key that is generated from a combination of the type and property name.

I wrote the whole library using a TDD approach.  You can see the full source code on the github page. If you are interested in digging in more detail I would suggest you start by looking at the ProxyInterceptor class. This is where the magic happens.