Simple BDD Testing

I've been messing around with BDD for years, sometimes obsessively evangelizing Dan North's concept and the idea of a ubiquitous language.  There's something about sharing specifications in a way that both developers and business people can understand that makes a ton of sense to me.  

I've always lamented the complexity of frameworks like Specflow, or the weird non-bdd language of frameworks like machine.specifications, or the fact that pretty much every test framework out there has the opinion that tests have to be methods.  I tried my best and built an overly complicated solution, called Given, closely modeled after mspec's idea of delegates as tests, but with words that more closely followed Gherkin syntax.  It had lots of problems, but it actually ended up being used by a surprising number of people.  My latest problem with it is that its solidly a .net framework library, and I don't plan on porting it over, because it is too much work and... I'm pretty lazy.

And that brings us to today.  I decided a few days ago to code golf a solution for bdd style testing that would work with xUnit and dotnet core.  I had a few goals:

  • Delegates for given, when and then syntax
  • NO STATIC FIELDS
  • No attributes on test classes

In the end I managed to hit those goals with a little creativity.  Here's an example test:

public class building_a_toyota_corrola : Scenario<FactoryContext>
{
    given a_toyota_factory = _ => _.Factory = new CarFactory("Toyota");

    when building_a_corolla = _ => _.Car = _.Factory.Build("Corrola");

    then the_car_should_be_made_by_the_correct_manufacturer = _ => _.Car.CarType.Should().StartWith("Toyota");

    then the_car_should_be_the_correct_make = _ => _.Car.CarType.Should().EndWith("Corrola");
}

 

and here is the "framework" involved in making that work:

public abstract class Scenario<T> where T : class, new()
{
    protected delegate void given(T context);
    protected delegate void when(T context);
    protected delegate void then(T context);

    public void RunScenario()
    {
        var context = new T();

        var fields = GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance).ToLookup(x => x.FieldType);
            
        fields[typeof(given)].ToList().ForEach(x => ((given)x.GetValue(this)).Invoke(context));
        fields[typeof(when)].ToList().ForEach(x => ((when)x.GetValue(this)).Invoke(context));
        fields[typeof(then)].ToList().ForEach(x => ((then)x.GetValue(this)).Invoke(context));
    }
}

For the delegate requirement, I've never actually created nested in a class, protected, generic delegates like this before, so I thought that was pretty neat.  We create an instance of a class of type T and then pass it into each of the delegates as shared state.  That solved for the static fields on my test classes.    The final requirement was no attributes on my test class.  I met that by putting the attribute on the base class method.  Pretty simple, and, I think, clean.

Its not without downsides though.  The biggest downside is that every scenario is represented by one test in your test runner.  My older framework would have made each then a line item in the test runner. It also won't output anything to the test runner with the gherkin.  Ideally I'd have wanted to put the gherkin representation of the test into the test output, marking specifically which then caused the failure, but xUnit is has some weird output writer injection stuff that requires a non-default constructor, and it was ugly so I abandoned it.  Overall, I think the simplicity and ease of writing the tests outweighs the downsides for me.