Astoria, SSIS Adapters and BDD – On BDD and MSpec

Let me preface this post by saying that it is about my (early) experiences practicing BDD in the context of a specific specification framework – MSpec.  There are plenty of folks out there who are much more authoritative on this subject than I.  And if you happen to be one of those people, I would love to get your feedback on what I’ve done here and how I could do it better.

In developing a SSIS data source to talk to an ADO.NET Data Services endpoint, shifting my approach from a basic TDD style to specifications was not originally part of the plan.  However, as I got deeper into development, I felt as though I actually wanted a bit more rigor in terms of semantic restrictions (e.g. arrange/act/assert) than I was getting out of the box from my unit test framework.  MSpec had been on my radar for a little while (I was introduced to both this framework and BDD in general at ALT.NET), so I took a quick look – and after staring oddly for a moment at the =()=> syntax decided that it provided the kind of semantic consistency I was really looking for.  Additionally, the fact that it could plug into the ReSharper unit test runner (with underscores removed from boxcar casing no less!) made it quick and very easy to get up and going on the journey writing specifications rather than tests.  Going through the specification writing exercise on this project has me convinced at this point that as someone else once said, BDD is TDD done right.  I certainly plan to keep pursuing specifications over traditional unit tests in future efforts.

So where to start?

I’m not going to spend time in this post talking about how to use MSpec – there’s already lots of good guidance out there.  That said, because the syntax initially looks a bit funky, I will say 2 quick things that are helpful to keep in mind when you first look at specifications built on top of MSpec.

  1. Everything in your specification (test) class is a field.  And all of the work  gets down via delegate fields which are of types with names like “Establish”, “Because”, and “It”
  2. Regarding the =()=> syntax, you are probably already comfortable with lambda expressions, so you can ignore the => on the right – and you can get the assignment operator on the left.  Therefore, that just leaves the empty parenthetical – and this just means that your delegate (lambda) is for a function that has no arguments.  There, all better?

I already had some unit tests written – though some of them were more of integration tests and revealed some pretty serious problems in my design – but we’ll deal more with those topics later.  So my starting point was to try and re-write some of my existing tests as specifications.  For example, take the following:

[Test]
public void Given_Null_URI_Throw_Exception() {
    try {
        new DataServicesQuery(_dataServicesQueryBuilder, null, "Suppliers");
    }
    catch(ArgumentNullException ex) {
        Assert.AreEqual("serviceUri", ex.ParamName);
    }
}

Re-writing this as a specification resulted in the following:

public class when_service_uri_is_null : with_dummy_resource_builder_and_reader
{
    static NullReferenceException lastException;

    Because service_uri_is_null = () => {
        try {
            queryBuilder.ServiceURI = null;
            queryBuilder.ResourcePath = "Products";
            queryBuilder.GetQuery();
        }
        catch (NullReferenceException ex) {
            lastException = ex;
        }
    };

    It should_have_specific_error_message = 
        () => lastException.Message.ShouldEqual("Service URI must be provided");

    It should_raise_an_exception = 
        () => lastException.ShouldNotBeNull();
}

A couple things to point out here. 

  • First and foremost, there is a more rigid semantic that is enforced by both convention and even by the type system.  The convention is that classes follow a “when X [with Y]” pattern (I found that this class name should map very tightly to the instructions contained in the Establish and/or Because expressions – if it does not, you may have a problem in your design).  Also, assertion (“It” expressions) delegates should follow a naming convention whereby “should …” is followed by a description of the assertion.  MSpec also uses the type system to enforces this convention in how the delegate types are named (e.g. – “Because”, “It”).
  • All of the NUnit attributes identifying test fixtures and tests are gone.  MSpec is smart enough to figure out what is a test vs. what is a regular old class based on the MSpec delegates that make up the class.  When looked at through the NUnit lens, the specification class remains the test fixture, and each “It” delegate instance becomes an NUnit test.
  • Notice the with_dummy_resource_builder_and_reader class.  As with unit tests, inheritance can be a great way to provide shared context to multiple test fixtures.  This base class is responsible for 2 things: 1) exposing a few data fields for its child classes, and 2) providing its own Establish delegate to setup those data fields.
  • For me, this format of specifications seemed to yield another benefit – without explicit effort.  That benefit was more tests with each test having a much better definition around its responsibility.

This particular type of test – one where you’re expecting an exception – was one where I wasn’t 100% confident in my approach.  However, I didn’t find any baked in support from MSpec – and frankly, for the framework to provide a feature similar to NUnit’s ExpectedExceptionAttribute doesn’t really make a lot of sense to me for writing specifications anyway.  You can see above the approach that I took – simply trap the exception and then run a couple tests over the exception to assert various characteristics of it.  If there’s a better way to go about this, I would love to hear it.

After I started getting the hang of writing specifications by rewriting my tests, I began venturing into new parts of my application – and this is where I really started realizing just how much ceremony exists in traditional unit testing frameworks compared to something like MSpec.  Because mostly everything in MSpec is a field, there’s no need to provide any kind of method body, etc. after declaring the title of the specification.  So, for example, when I wanted to write specifications around executing a query, I was able to quickly capture the specs as follows:

public class when_query_is_executed_for_products_with_expanded_suppliers
     : with_query_shape_factory
{
    It should_have_2_result_sets;
    It should_have_a_result_set_named_products;
    It should_have_8_columns_in_products_result_set;
    It should_have_77_rows_in_products_result_set;
    It should_have_a_result_set_named_suppliers;
    It should_have_13_columns_in_suppliers_result_set;
    It should_have_77_rows_in_suppliers_result_set;
}

Remember, “It” is simply a type and “should_have_2_result_sets” is simply a field declaration – no need to initialize it with anything just yet.  When the MSpec runner is executing, it checks to see whether the field has been initialized – if it hasn’t, as in this case, it flags it as if you used NUnit’s IgnoreAttribute.

Finally, I mentioned earlier in this post about using ReSharper to run MSpec specifications.  While I would probably still be interested in MSpec if this integration didn’t exist, the fact that it was there removed any question or hesitation.  We talk a lot about the readability of tests, and of tests being the spec – and this is, based on what I’ve experienced so far, where specification frameworks like MSpec really shine.  Bringing that goodness directly into Visual Studio ala ReSharper mades the feedback loop even tighter, and really made for a happy development process.

Untitled

If you’ve read this far and you have no clue what this project is that I’m talking about, see the overview here.  If you want to download the code and look at it yourself, check it out at http://code.msdn.microsoft.com/ssisastoria

About Howard Dierking

I like technology...a lot...
This entry was posted in ADO.NET Data Services, BDD, R#, SSIS. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://codebetter.com/members/hdierking/default.aspx hdierking

    @Aaron

    thanks for the detailed feedback! A few comments -

    WRT the name of the Because field – I had started out using *of* per the examples, and I’m now comfortable enough with how the framework and my code express my intentions to go back to it – however, in the course of practicing BDD, I found it helpful to restate the action in the Because field name as a sanity check to ensure that I actually was specifying what I said I was going to specify in the class name.

    WRT the base class – thanks for that link. I’m using context base classes mostly for setting up mocks (more specifically, reading metadata from a file rather than from a live service) – so it makes sense in my head :) – still, your suggestion of possibly putting such activities in a helper method and calling it from multiple contexts has given me something to think about. I’ll follow up in another post exploring what the different options might look like.

    WRT exception catching – yup, works as advertised! The class above would be re-written as follows:

    public class when_service_uri_is_null : with_dummy_resource_builder_and_reader
    {
    static Exception lastException;

    Because service_uri_is_null = () =>
    lastException = Catch.Exception(() =>{
    queryBuilder.ServiceURI = null;
    queryBuilder.ResourcePath = “Products”;
    queryBuilder.GetQuery();
    });

    It should_have_specific_error_message =
    () => lastException.Message.ShouldEqual(“Service URI must be provided”);
    It should_be_of_type_null_reference_exception =
    () => lastException.ShouldBeOfType();
    It should_raise_an_exception = () => lastException.ShouldNotBeNull();
    }

  • http://sergiopereira.com/blog Sergio Pereira

    Excellent blog Howard. It’s still rare to come across a clear explanation and perspective on BDD. This helps a lot.

  • http://MurrayOn.NET/ Mike Murray

    I”m learning BDD (via MSpec) as well. I’m actually still pretty new to TDD, but already feel BDD is TDD done right, in that it guides you better in your design choices and holding you off from committing to a specific implemenation before you should.

    My post series about doing Pragamatic Programmer Dave Thomas’ Code Kata 6 (Anagrams) is here: http://murrayon.net/2009/11/anagram-code-kata-bdd-mspec.html

  • http://codebetter.com/members/aaronjensen/default.aspx aaronjensen

    Great post Howard! Always good to hear someone’s first experiences with the framework.

    A few things I noticed, and maybe you can help me come up with a way to make these things more clear in the framework (or maybe I just need.. uh, documentation? heh):

    * The name of the Because field: typically I just go with “of” (Because of =()=>)but I noticed some people are putting verbage in there. Adding verbage there is a little awkward since it’s really just repeating the context (name of the class). Is it something you feel adds value or did you do it because you had the impression that was how it was intended?

    * The base classes are not a necessity. I should really rework the example specs in mspec to remove this. I feel they lead people down a path of hiding too much in the base class and relying too much on the name of the base class. I like to use base classes to hide cruft–helper methods, static field declarations, boring initialization etc. Any essence of the context’s setup should be in the subclass. I discuss this a bit more here: http://codebetter.com/blogs/aaron.jensen/archive/2009/10/05/a-recent-conversation-about-mspec-practices.aspx

    * Exception catching: MSpec actually does have a mechanism for this built in, but you’re the second person in the last week to not see it, for which I blame only myself :) Basically you’d do this:

    Because of =()=>
    exception = Catch.Exception(()=>IThrow());

    It should_fail =()=>
    exception.ShouldNotBeNull();

    or
    exception.ShouldBeOfType();

    etc. Your approach is the same effectively, there’s just that helper method.

    Again, thanks for writing this up!