Welcome to EMC Consulting Blogs Sign in | Join | Help

James Broome Blog

Asp.Net MVC Controllers + BDD = The perfect match? [Part #1: The HomeController]

This is part 1 in a series of posts on using Behaviour Driven Development to build and test your MVC controllers. The full series is as follows:

 

Behaviour Driven Development/Design has been gaining a lot of traction recently amongst the community and whilst not everyone may be using it in anger to write code and deliver projects, most people should have a fair idea of what it is all about by now. I’ve been talking about it for a while, since I attended JP Boodhoo’s Nothin But .Net course over a year ago, where I was exposed to BDD for the first time. For the last 6 months or so, I’ve been Dev Lead on a greenfield e-commerce application, using Asp.Net MVC, Sharp Archictecture, N2 CMS, NHibernate, Spark View Engine, Post Sharp and a whole host of other cool stuff. However the main thing for me has been that it has been the first project where I’ve been able to use BDD properly, the result of which is that we’ve written more tests that any other project I’ve been on and because of that the quality of the code we’re producing is getting higher and higher each sprint.

BDD has a real sweet spot when it’s used to describe high level, user story-like business specifications. With MVC, this can be used to great effect when building out the controllers layer of your application. The controllers are what handle user actions and inputs to the application, determining what the correct result should be e.g. displaying a view, redirecting to another action. In a nutshell the controllers are what govern the flow of your application from the end users perspective, which makes them ideal candidates for BDD style specifications.

The out-of-the-box Asp.Net MVC project (File | New Project) comes with some pre-built controller functionality – the HomeController and the AccountController. This allows you to build a MVC application with user authentication very quickly and, although most people will choose not to use this code for anything other than a simple web app, it provides a set a business rules and functionality that everyone is familiar with.

So, to illustrate my point, I decided to use BDD to create a series of specifications for the out-the-box HomeController and AccountControlller functionality. Whilst this is a slightly backwards excercise as the functionality for these controllers already exists, hopefully this will show how you could continue in this way to build up the other controllers in your application.

 

Pre-Requisites

We’ve been using JP Boodhoo’s style of BDD specifications on my current project and whilst this has proved successful, during this time, JP has been adapting and pushing his style and syntax to something even more terse and fluent. He now has the developwithpassion library available up on GitHub which I’ve used for this excercise. It’s been a bit of a jump from the style and syntax that I’ve been using on my project, but I’m really happy with the results. I’ll post a fully working code sample at the end of the series with everything you need to run the specs.

 

First up – the HomeController.

If we imagine that the HomeController did not already exist, our first requirement would be that we needed something to handle the overall default action on the site – i.e. what happens when someone just browses to http://mysite.com ? We now know that we need a HomeController and that it’s default action should be to display the home page of the site. We also know (because it already exists) that our home page view should display a message – “Welcome to ASP.NET MVC!”.

So, our first scenario is:

When the home controller is told to display the default view

  • It should display the home page view
  • It should display the welcome message in the view

Translating this into code, I came up with the following specification

[Concern(typeof (HomeController))]
public class when_the_home_controller_is_told_to_display_the_default_view : observations_for_a_sut_without_a_contract<HomeController>
{
    static string key;
    static string message;
    static ActionResult result;

    context c = () =>
    {
        key = "Message";
        message = "Welcome to ASP.NET MVC!";
    };

    because b = () =>
        result = sut.Index();

    it should_return_the_home_view = () =>
        result.is_a_view_and().ViewName.should_be_empty();

    it should_display_the_welcome_message_in_the_view = () =>
        result.is_a_view_and().ViewData[key].should_be_equal_to(message);
}

The fluent syntax in the BDD specifications is mostly straight from JP’s developwithpassion library, which uses extension methods to the extreme to wrap MbUnit and RhinoMocks. I added a couple of MVC ActionResult specific extension methods to help with checking the results of the actions by casting them to their expected types first:

public static class ActionResultExtensions
{
    public static ViewResult is_a_view_and(this ActionResult result)
    {
        return (result as ViewResult);
    }

    public static RedirectResult is_a_redirect_and(this ActionResult result)
    {
        return (result as RedirectResult);
    }

    public static RedirectToRouteResult is_a_redirect_to_route_and(this ActionResult result)
    {
        return (result as RedirectToRouteResult);
    }

    public static string controller_name(this RedirectToRouteResult redirect_result)
    {
        return redirect_result.RouteValues["Controller"].ToString();
    }

    public static string action_name(this RedirectToRouteResult redirect_result)
    {
        return redirect_result.RouteValues["Action"].ToString();
    }

    public static void should_be_empty(this String the_string)
    {
        the_string.should_be_equal_to(string.Empty);
    }
}

I’m also relying on the convention over configuration approach of MVC in that if you don’t pass in a view name to an ActionResult then it will use the name of the calling action, so when I say:

it should_return_the_home_view = () =>
    result.is_a_view_and().ViewName.should_be_empty();

I’m enforcing that the view name is the same as the action name.

We’ve now got the default action of the HomeController covered and if these specifications were executed, they would pass based on the out-the-box MVC project HomeController functionality:

[HandleError]
public class HomeController : Controller
{
    public ActionResult Index()
    {
        ViewData["Message"] = "Welcome to ASP.NET MVC!";

        return View();
    }

    public ActionResult About()
    {
        return View();
    }
}

Our second scenario (which we know by looking at what the HomeController already does!) is that we need an about page. We (already) decided to handle this by adding an About action to the HomeController.

When the home controller is told to display the about view

  • It should display the about page view

Simple really. Try to remember that normally, the HomeController would not already have this functionality so we would be driving this out from the spec. Translating this into code and following the conventions I had previously I came up with the following specification:

[Concern(typeof(HomeController))]
public class when_the_home_controller_is_told_to_display_the_about_view : observations_for_a_sut_without_a_contract<HomeController>
{
    static ActionResult result;

    because b = () =>
        result = sut.About();

    it should_return_the_about_view = () =>
        result.is_a_view_and().ViewName.should_be_empty();
}

Ok, so this isn’t rocket science, and the HomeController doesn’t really do much, but if we wanted to add a help page for example then we could go on in similar fashion for adding the spec and then adding the action onto the HomeController. Or, if we decided to change the default home page view (e.g. to display the current date, or the logged in username, or a different view for every day of the week), then we can add to or modify our HomeController specifications accordingly.

 

Next Time…

I’m really happy with these controller specs and creating them for the HomeController, although simple, has given me a style and convention of how I’m going to approach the other functionality in our out-the-box MVC application. In the next post, I’ll start to look at the AccountController, where things get a bit more interesting. Hopefully this has shown how powerful BDD can be when talking at the controller level of an application.

UPDATE - Due to wierd formatting issues with the code snippets, I'd recommend reading this in Chrome, which seems to give the best experience!

@broomej

 

Bookmark and Share
Published Wednesday, September 16, 2009 4:12 PM by james.broome

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

 

Vogue roma said:

This is such a great resource that you are providing and you give it away for free. I love seeing websites that understand the value of providing a quality information. Thanks for sharing.

February 24, 2011 9:13 AM
 

Capodanno roma said:

Hi, Thanks for sharing such a wonderful piece of information. I must say that while reading your post I found my thoughts in agreement with the topic that you have discussed, which happens very rare.

February 24, 2011 9:16 AM
 

Recruitment Software said:

Hi, Thanks for sharing such a wonderful piece of information. I must say that while reading your post I found my thoughts in agreement with the topic that you have discussed, which happens very rare.

February 24, 2011 12:47 PM

Leave a Comment

(required) 
(optional)
(required) 
Submit

About james.broome

James is a Technical Consultant at Conchango
Powered by Community Server (Personal Edition), by Telligent Systems