Programming  /  Testing August 04, 2010

Using MSpec – a few weeks in

About six weeks ago I blogged about my infatuation with the relatively new testing framework MSpec (short for Machine.Specifications). Today I got a comment on the post from Colin with an improved version of the example test that I had posted. That reminded me that it’s about time that I post a report on how my experiments with MSpec has been faring and a few lessons that I’ve learned.

Me and a colleague have been using MSpec on a web project since I wrote that post and are now up to about 230 individual specifications. Not to shabby considering that we’ve both been on vacation the last three of those six weeks.

Anyhow, having used MSpec for a while now I’ve sort of found a style for structuring and writing my tests/specifications.

An It should only be a single line

The example that I posted in my first post about MSpec was a very basic specification that an action method in an (ASP.NET MVC) controller returned the default view name (empty string):

[Subject(typeof(LogoController))]  
public class when_index_action_is_invoked
{
    static LogoController controller;

    Establish context = 
        () =>
            {
                controller = new LogoController();
            };

    It should_return_a_ViewResult_with_default_view_name = () =>
    {
        var result = controller.Index();

        result.ViewName.ShouldBeEmpty();
    };
}

The improved version that Colin posted looked like this:

[Subject(typeof(LogoController))]
public class when_index_action_is_invoked
{
    static LogoController controller;
    static ViewResult result;

    Establish context =
    () =>
    {
        controller = new LogoController();
    };

    Because of = () => result = controller.Index();

    It should_return_a_ViewResult_with_default_view_name = () =>
    {
        result.ViewName.ShouldBeEmpty();
    };
}

The difference with this solution is that the result is stored in a class variable and populated by a delegate of type Because. This offers two improvements. One is the introduction of the Because delegate which makes the test easier to understand as it splits it up in three natural phases, establishing the context, performing an action and finally stating/verifying a result. These three phases are usually referred to as Arrange, Act and Assert (AAA) or Given-When-Then.

The second improvement is that the It is a single line which makes it abide by the official tip to “Make your Its/Becauses single-line statements” in the readme for MSpec that states that “MSpec is designed to reduce noise in tests. You should generally only have one line in your It and Because statements. As such, you should probably leave out the { and }”.

I read that about a week after having posted the first post about MSpec. At that point I was usually having multiple lines in my Its that verified different but related things, like this:

It should_contain_a_link_to_the_current_page
    = () => 
    {
        breadcrumbs.First().LinkUrl.ShouldEqual(page.LinkUrl);
        breadcrumbs.First().Text.ShouldEqual(page.Name);
    }    

The above It is from a specification for a presenter (in a MVP scenario) for a bread crumbs widget and states that the bread crumbs should contain a link to a specific page. Now, when we read this code it’s quite obvious that that means that the link should contain the page’s name and link to it’s URL. However, when we generate a specification from this those details are lost. That means that a domain expert/customer that we show it to won’t get all the relevant details and we might miss out on important feedback. It also means that if the test fails we’ll get a less detailed report on what is wrong. Therefore I quickly found that forcing myself to only have a single line per It was really helpful. Split up in two that It now looks like this:

It should_contain_a_link_to_the_URL_of_the_current_page
    = () => 
        breadcrumbs.First().Text.ShouldEqual(page.PageName);

It should_contain_a_link_with_text_equal_the_current_pages_name
    = () => 
        breadcrumbs.First().LinkUrl.ShouldEqual(page.LinkUrl);

Create and name specifications from a users perspective

Another thing that I would had done different with the example from my earlier post is how I named the class and the It delegate. In that example the class was named when_index_action_is_invoked and the It was named should_return_a_ViewResult_with_default_view_name. This produces the following specification/output when running the test:

LogoController, when index action is invoked
» should return a ViewResult with default view name

This doesn’t make a lot of sense if I try to explain it to an editor, administrator or user of the site even if that person knows a bit about how the web works. Compare that to the breadcrumbs example which (along with a couple of other Its that I omitted before) produces the following output:

Breadcrumbs, when viewing a page that is child of the startpage
» should be visible
» should contain a single link
» should contain a link to the URL of the current page
» should contain a link with text equal the current pages name

All of the statements above is understandable to a non-programmer and can therefore help bridge the gap between me and the domain expert (in this case probably an editor) as the domain expert can look at this specification and actually determine if the BreadCrumbsPresenter class (which is what’s being tested but the domain expert doesn’t need to know that) is working correctly given the scenario.

So, what does that mean for the LogoController specification in the original example? Well, renaming it so that a domain expert can understand it is pretty hard. I really don’t know how to explain what it means that the view name is empty in terms of what a visitor sees. To me that’s an indication that the specification really isn’t needed. And I think that that’s true, it really isn’t needed. Such a detailed specification for a purely technical detail will probably only make the code harder to work with. It might provided code coverage, but that type of coverage is rarely useful in my experience.

Group specifications across multiple classes

In the web project that I’ve been working on we’re using Web Form MVP (as we’re using EPiServer CMS which doesn’t yet support ASP.NET MVC) and it’s quite common that we have logic in both our page types (basically domain model objects) and our presenters that aren’t technically related but very closely related conceptually for a domain expert. So far I’ve found that I like to keep the tests for the presenters separate (in separate classes in a different namespace) from the tests for the page types. However, instead of using the Subject attribute to specify the type of the class under test I’ve started using it to specify what is being tested from a users perspective. That is, instead of using it like this:

[Subject(typeof(EditorialArticlePresenter))]
//Some test for the presenter

[Subject(typeof(EditorialArticle))]
//Some test for the page type

I now use a string to specify what is being tested from the point of the user, like this:

[Subject("Editorial article")]
//Some test for the presenter

[Subject("Editorial article")]
//Some test for the page type

What’s nice about this is that when I run the test and generate a specification the specifications for the presenter and the page type get grouped together.

More than a crush

In the previous post I asked “Will the initial romance last and evolve into real love or is it just a temporary crush?”. Well, a few weeks later I’m still very much in love with MSpec. With that said I’m always looking at and for alternative and better ways of doing things, but for web projects and other types of projects where the customer isn’t a programmer I definitely think using MSpec offers an improvement over xUnit.net/NUnit which I usually use.

PS. For updates about new posts, sites I find useful and the occasional rant you can follow me on Twitter. You are also most welcome to subscribe to the RSS-feed.

Joel Abrahamsson

Joel Abrahamsson

I'm a passionate web developer and systems architect living in Stockholm, Sweden. I work as CTO for a large media site and enjoy developing with all technologies, especially .NET, Node.js, and ElasticSearch. Read more

Comments

comments powered by Disqus

More about Testing