Using Behaviour-Driven Development with ASP.NET MVC – Part 1

Introduction

Welcome to the first post in a series where I hope to demonstrate creating an ASP.NET MVC application using Behaviour-Driven Development (BDD).

I agonised over how to introduce this blog post. In the end, I figured it would be best just to get straight into it! There are plenty of excellent articles that introduce the concepts behind both BDD and ASP.NET MVC, so I won’t bother repeating it here. I will explain core concepts along the way and provide links to further information. If you feel that I haven’t explained something properly, or have missed something, then please post a comment and I’ll try to elaborate further.

Right, let’s get started. The application I am going to build is a playlist sharing web site aptly called PlaylistShare (slightly unimaginative, but if you have a better name, let me know! :-). Music streaming services like Spotify are becoming increasingly popular and a number of sites have sprung up that allow you to share playlists with other people. I don’t intend this example to be anything unique or new, but I thought it would be an interesting subject and not yet-another-storefront example.

I will provide a link to download the source code at the end of each post. I have also created a Google Code project to host the source code.

It All Starts With a Story

BDD is an outside-in development process. We start by identifying goals and motivations, then drill down to the features that will achieve those goals. So, I’ve come up with a few user stories to get us started.

Story: Listener views playlists

As a playlist listener, I can view a list of playlists that have been submitted, so that I can find a playlist I might enjoy.

Story: Listener views playlist details

As a playlist listener, I can view the playlist details, so that I can get more information about the playlist.

Story: Contributor submits a playlist

As a playlist contributor, I can submit a playlist, so that others can listen to it.

There are a lot of other features I can add, but as a “business”, these are my top priorities and will allow me to get something functional delivered first.

Defining Acceptance Criteria

In order to determine what behaviour to implement, we need to create a list of acceptance criteria for each story. The acceptance criteria allows us to define when a story is “done”.

Note: this is a very important stage. It allows all members of the team to share a common understanding of what the system is meant to do. It is tempting to jump straight to writing the executable specifications, but this stage needs to be carefully considered and have input from the customer and testers.

We will define the acceptance criteria as example scenarios using a given/when/then vocabulary.

Story: Listener views playlists

Scenario 1: Viewing a list of playlists
Given a list of playlists
When the listener views the playlists
Then should display a list of playlists
  And playlists should be ordered by date submitted from newest to oldest

Story: Listener views playlist details

Scenario 1: Viewing playlist details
Given a playlist
When the listener views the playlist details
Then should display the playlist details
  And should display a link to listen to the playlist

Story: Contributor submits a playlist

Scenario 1: Submitting a playlist
Given a playlist
When the contributor submits the playlist
Then should submit the playlist
  And should display a message confirming the submission was successful

Scenario 2: Submitting a playlist without a name
Given a playlist without a name
When the contributor submits the playlist
Then should display a message saying the name is required
  And should not submit the playlist

Now that we have defined some acceptance criteria, we can create the executable specifications used to drive the implementation (a fancy way of saying, “write some tests!”). As we go forward we will probably identify other features we would like. We will make a note of these then add them to our next “iteration”.

Writing Executable Specifications

In BDD, tests are called specifications. This is mainly to get away from the notion that we are “testing”. Rather, we are defining behaviours that will drive the development process. We will still end up with a full suite of automated tests, but this is merely a very handy side-effect, not the goal, of BDD.

There are a number of BDD frameworks available for .NET, the most well-known of these being NBehave. However, these frameworks can be difficult to get started with and can distract us from the main purpose of BDD, which is to effectively describe the application behavior. This is not because these frameworks are bad, but is more to do with the constraints of the C# language. BDD frameworks are much more effective in languages like Ruby, where the syntax makes it very easy to write readable specifications using frameworks such as RSpec and Cucumber. For this example, I am going to stick with using NUnit and structure the test classes and member names to enable us to write BDD specifications. It will be a glorious day when when eventually see RSpec and Cucumber working on IronRuby and integrated seamlessly with our .NET applications. Until then, this is my current preference for writing BDD-style specifications in real-world applications.

I use a common Specification base-class to provide a framework for writing the specifications. This simple class contains the NUnit setup/teardown methods, handles exceptions and coordinates the various stages of executing a scenario.

These are a few conventions I use when writing BDD-style specifications:

  • Specification names are lowercase with underscores. This_is_to_make_the_specifications_more_readable. It is a personal preference, but I find PascalCaseNamingBecomesHardToReadForLongSentences (see what I did there… heh).
  • One test-fixture per scenario. Each test fixture represents a scenario of the story.
  • Each test-fixture is wrapped in a namespace that describes the scenario. This allows us to easily navigate the scenarios and prevents naming conflicts with other actions. This becomes important when we have the same action in different scenarios.
  • Each test-fixture name defines the action. An action is the “when” part of a scenario. It is the event that causes the behaviour.
  • Each test-fixture derives from a base class that describes the story. By inheriting from a base class, we can provide common set up data that is shared across all scenarios in the story. It also provides us with a handy reference back to the original story.
  • The context is set before executing the action. The context is the “given” part of a scenario. The context sets the state of the system-under-test and its dependencies before the action is invoked. Wrapping setup code in helper-methods makes it easy to see what context we are creating and to reuse common setup steps.
  • One test per outcome. An outcome is the result of an action, the “then” part of a scenario. We may have many outcomes from an action, but we should only be testing for one thing per outcome. So try to only make one assertion per test method. Test for other outcomes in separate test methods.

The outline for the first scenario looks like this:

namespace view_playlist_specs
{
    namespace scenario_of
    {
        public abstract class listener_views_playlists : Specification<PlaylistController>
        {
            protected override PlaylistController create_subject()
            {
                // Create the system under test
                return new PlaylistController();
            }
 
            protected void given_a_list_of_playlists()
            {
                // Establish a list of playlists
            }
        }
    }
 
    namespace viewing_a_list_of_playlists
    {
        [TestFixture]
        public class when_the_listener_views_the_playlists 
            : scenario_of.listener_views_playlists
        {
            protected override void setup_scenario()
            {
                // Arrange
                given_a_list_of_playlists();
            }
 
            protected override void execute_scenario()
            {
                // Act
            }
 
            [Test]
            public void should_display_a_list_of_playlists()
            {
                // Assert
            }
 
            [Test]
            public void playlists_should_be_ordered_by_date_submitted_from_newest_to_oldest()
            {
                // Assert
            }
        }
    }
}

Download the source code containing the outline for the first scenario.

I hope this has been a helpful way to get started. I will explain a lot more about writing specifications in upcoming posts. Please feel free to post any questions or comments. In the next part of this series, I will begin implementing a playlist controller and passing the scenarios.

About these ads

8 Responses to “Using Behaviour-Driven Development with ASP.NET MVC – Part 1”


  1. 1 Jen May 7, 2009 at 8:18 am

    I have tried moving to your namespace-syntax with some of my tests and it is looking a lot better. Still agonising over one-assert-per-test as it is increasing the copy-pasting I am having to do…

    But anyway, will be interesting at the end of this if you could maybe think about re-implementing the production code using OpenRasta or something (I went to the VBUG talk – I am now a fan!). Will be a good test of how platform-agnostic you can make the tests!

  2. 2 timross May 7, 2009 at 10:05 am

    Hi Jen, I have found adding the scenario namespace makes it easier to define the specifications. You also don’t find yourself appending so much context information to a test-fixture, so when_user_views_shopping_cart_and_user_is_not_logged_in_and_cart_is_empty becomes a namespace of viewing_an_empty_cart_when_not_logged_in and a test-fixture of when_viewing_cart.

    The one-assert-per-test is just a guideline. You should only be testing for one outcome per test. However, you may find that one outcome needs two-or-more assertions. For example, you are returning a list of customers. You might need to assert that the list is both not-null and contains customers. In this case, testing for null is not really something that warrants a separate test method. When the test fails it should be easy to identify the cause. Tests with several assertions testing all kinds of outcomes is the antithesis of this, as it can be hard to identify what the expected outcome is.

  3. 3 guillaume January 28, 2010 at 4:52 pm

    hello
    a very rare (and very good) article to talk about BDD and ASP.Net MVC

    you said that you’re waiting to see a framework like Cucumber work with .Net
    now it’s seems to work

    http://hotgazpacho.org/2009/06/cucumber-and-ironruby-it-runs/

  4. 4 Now i'm May 11, 2011 at 11:50 pm

    This is the best weblog for anyone who wants to know about this topic. You know your stuff. Its clear you’ve carried out your research. You definitely put a brand new spin on a topic thats been written about for years. Fantastic stuff, just fantastic!

  5. 5 Sam Goward April 23, 2013 at 4:53 am

    This is indeed very helpful to getting started! You defined criteria in different scenario in best way.


  1. 1 Reflective Perspective - Chris Alcock » The Morning Brew #341 Trackback on May 6, 2009 at 7:35 am
  2. 2 9eFish Trackback on May 6, 2009 at 12:39 pm
  3. 3 Using Behaviour-Driven Development with ASP.NET MVC – Part 2 « Tim Ross – .NET Developer Trackback on May 8, 2009 at 2:30 pm

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s





Follow

Get every new post delivered to your Inbox.

%d bloggers like this: