Electric Dreams

Tim Long on Technology, Science, Music, Astronomy, Software, Business and life as a Microsoft Small Business Specialist

Recent Posts

Tags

News

  • Locations of visitors to this page
    View Tim Long's profile on LinkedIn
    Tim Long
    StackOverflow.com
    Serverfault.com
    Astronomy Answers

Community

Email Notifications

TiGra Networks

My Family

Photo Galleries

SBS Groupies

Archives

Test Driven Development with MSpec

I’ve been changing the way I develop software over the last few months. It has been a somewhat difficult but worthwhile journey for me and I thought I’d share some of what I’ve learned.

Why?

Unit testing and test driven development serves several purposes. It provides a way of showing that the software you develop is verifiably correct; it provides a means of regression testing; it is a discipline that means you write the minimum amount of code to perform the required function.

What’s a Unit Test?

From Wikipedia, unit testing is “a method by which individual units of source code are tested to determine if they are fit for use. A unit is the smallest testable part of an application”. This usually means a single method, function or subroutine. Each ‘unit’ is tested in isolation, it shouldn't depend on any previous tests nor should it affect any subsequent tests. Making software that can be ‘broken apart’ and tested in isolated units requires a certain discipline in the way the software is constructed, and also requires things such as fakes, stubs and mocks which form a sort of ‘scaffolding’ around the code being tested. These concepts are quite closely related and in practice, unit tests will be constructed using a unit testing framework together with a mocking framework.

The unit testing framework I’ve chosen (Machine.Specifications or ‘MSpec’) is perhaps one of the lesser known varieties, but in my opinion deserves much wider uptake. MSpec can happily be used as a unit testing framework, but works best in a Behaviour Driven Development (BDD) model, and it is this BDD philosophy that starts to bring about the real change in one’s approach to software development.

Machine.Specifications

I chose Machine.Specifications (MSpec) because I like the way it allows for the creation of literally specifications. Although these are written in C# code, they can be very descriptive and initially don’t need to contain any code. This executable specification is readable enough that non-programmers could understand, or even write, specifications. The only way to really understand this is to see an example, so I’ll walk through a simple example of testing a bank account class. I’m no expert in this and I’m not claiming this is any example of best practice, but my intention is to provide a high level glimpse of how I go about testing with MSpec.

Getting Started

The easiest way I’ve found of getting MSpec working with your test project is to use the NuGet Package Manager – a very handy add-in for visual studio. Open the Package Manager Console and proceed as follows:

image

Simple as that. NuGet downloads the files, adds them to the solution and references the MSpec assemblies. Now to write our first specification.

using Machine.Specifications;

namespace BDD.Bank.Specifications
{
    [Subject("New Account")]
    public class when_creating_a_new_account
    {
        static Account target;                                      // The item under test.
        Establish context = () => { };                              // Arrange
        Because of = () => { target = new Account("Tim Long"); };   // Act
        It should_have_a_non_blank_accountholder_name;              // Assert
        It should_have_a_nonzero_account_number;
        It should_have_an_opening_balance_of_zero;
    }
}

A few things to notice about this code.

  • It follows the Arrange, Act, Assert mantra. In MSpec it is quite natural to have multiple assertions.
  • There are some strange language elements such as ‘= () =>’. That’s actually a Lambda expression that creates a delegate to an anonymous method. Establish, Because and It are all delegate types defined by MSpec. If that offends your senses, just think of it as some sort of operator that MSpec uses, you get used to it really quickly.
  • The assertions (It expressions) have no code in them. So this really is a specification rather than a test.
  • The Account type has not been defined yet (so this won’t compile).
  • You can probably understand what this test is doing, because it reads almost like English.

Next step is to define an Account class. Notice how the test is driving what code we write. I use ReSharper so a few clicks gets me an empty class definition for Account. Now I can run my test suite. I use ReSharper as my test runner, but MSpec also has its own test runner if you don;t have ReSharper. The output looks like this:

image

Filling In the Details

Now to flesh out the tests. Take the first item, should have a non-blank accountholder name. This, together with the Because clause, mandate that our Account class should have a constructor that accepts a string argument and that it stores that value in a property. I’ll call the property Name. Notice how the specification is again driving what code I write. My account class now looks like this:

namespace BDD.Bank
{
    public sealed class Account
    {
        /// <summary>
        ///   Initializes a new instance of the <see cref = "Account" /> class.
        /// </summary>
        /// <param name = "name">The accountholder's full name.</param>
        public Account(string name)
        {
            Name = name;
        }

        /// <summary>
        ///   Gets the accountholder's name.
        /// </summary>
        /// <value>The name of the account holder.</value>
        public string Name { get; private set; }
    }
}

Now, our test can be filled in:

        It should_have_a_non_blank_accountholder_name = () => target.Name.ShouldEqual("Tim Long");

ShouldEqual is one of many assertion methods provided by MSpec, note the fluent style. And the results:

image

Moving on to our second assertion, should have a nonzero account number. This mandates that the Account class should be capable of generating and storing an account number. At this point you may be tempted to dive into writing a large chunk of code to generate unique account numbers, but Test Driven Development best practice is to only write the minimum code necessary to pass the test, so I’m just going to set it to 1 in the constructor.

Similarly, should have an opening balance of zero mandates another property which I’ll set to zero in the constructor. My finished test fixture looks like this:

using Machine.Specifications; namespace BDD.Bank.Specifications { [Subject("New Account")] public class when_creating_a_new_account { static Account target; // The item under test. Establish context = () => { }; // Arrange Because of = () => { target = new Account("Tim Long"); }; // Act It should_have_a_non_blank_accountholder_name = () => target.Name.ShouldEqual("Tim Long"); It should_have_a_nonzero_account_number = () => target.Number.ShouldNotEqual(0); It should_have_an_opening_balance_of_zero = () => target.Balance.ShouldEqual(0); } }

The Account class thus far looks like this:

using System;

namespace BDD.Bank
{
    public sealed class Account
    {
        /// <summary>
        ///   Initializes a new instance of the <see cref = "Account" /> class.
        /// </summary>
        /// <param name = "name">The accountholder's full name.</param>
        public Account(string name)
        {
            Name = name;
            Number = 1;
            Balance = 0;
        }

        /// <summary>
        ///   Gets the accountholder's name.
        /// </summary>
        /// <value>The name of the account holder.</value>
        public string Name { get; private set; }

        /// <summary>
        ///   Gets the account number.
        /// </summary>
        /// <value>The account number.</value>
        public int Number { get; private set; }

        /// <summary>
        ///   Gets the account balance.
        /// </summary>
        /// <value>The account balance.</value>
        public Decimal Balance { get; private set; }
    }
}

And the test results like this:

image

That sea of green ticks is a really good feeling. Even though this is a very trivial example, I think it demonstrates some of the important aspects of test driven development and the way that MSpec can be used to write specifications that produce readable output. The way I write code has definitely changed as a result of investigating MSpec.

Further Reading

MSpec Resources - http://altnetseattle.pbworks.com/w/page/25113341/MSpec

Rob Conery – Learning Behaviour Driven Development (screencast)

Share this post: | | |