MSTestContrib is a set of extensions orientated towards software developers and testers who are using the MSTest framework which is included with Visual Studio 2010.

Specifications Step-by-Step

MSTestContrib contains a BDD-style specification framework that can be used in conjunction with the MSTest framework. One of the key benefits of the MSTestContrib specification framework is that it gracefully extends your existing use of MSTest. The following is a quick guide to getting started with the MSTestContrib style of BDD programming.

The following is a step-by-step guide to getting started with MSTestContrib Specifications. To follow along go and grab the latest release of MSTestContrib and add it to your project.

Step 1: Starting Point

In the following example we have an existing test written using the standard Arrange/Act/Assert pattern. As you can see it is testing a basic add function on a calculator class.

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Calculator.Core.Specifications
{
  [TestClass]
  public class BasicCalculatorTest
  {
    [TestMethod]
    public void AddTwoToACalculatorWithZeroOnTheAccumulator()
    {
      // Arrange.
      var calculator = new BasicCalculator();

      // Act.
      calculator.Add(2);

      // Assert.
      Assert.AreEqual(2, calculator.Accumulator);
    }
  }
}

Step 2: Assemblies and Namespaces

The first thing that you need to do when you start down the MSTestContrib Specifications path add a reference to the MSTestContrib.dll assembly and include the MSTestContrib.Specifications namespace in the source file.

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MSTestContrib.Specifications;

namespace Calculator.Core.Specifications
{
  [TestClass]
  public class BasicCalculatorTest
  {
    [TestMethod]
    public void AddTwoToACalculatorWithZeroOnTheAccumulator()
    {
      // Arrange.
      var calculator = new BasicCalculator();

      // Act.
      calculator.Add(2);

      // Assert.
      Assert.AreEqual(2, calculator.Accumulator);
    }
  }
}

Step 3: Attributes and Base Classes

MSTestContrib requires you to sub-class the MSTestContrib.Specifications.Specification class in your own unit test classes. In addition to sub-classing Specification you also need to decorate the class with the SpecificationDescription attribute and the test method with the ScenarioDescription attribute. These attributes are used to describe the nature of the specification (typically a user story) and the scenarios under which this specification applies (think pre-conditions and corner cases).

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MSTestContrib.Specifications;

namespace Calculator.Core.Specifications
{
  [TestClass]
  [SpecificationDescription("As a user I want to perform mathematical calculations so my head doesn't hurt.")]
  public class BasicCalculatorTest : Specification
  {
    [TestMethod]
    [ScenarioDescription("Add two to a calculator with zero on the accumulator.")]
    public void AddTwoToACalculatorWithZeroOnTheAccumulator()
    {
      // Arrange.
      var calculator = new BasicCalculator();

      // Act.
      calculator.Add(2);

      // Assert.
      Assert.AreEqual(2, calculator.Accumulator);
    }
  }
}

Step 4: Given/When/Then

The next thing that you need to do is re-cast the test in the form of a specification. This is perhaps the most important mind shift when moving from TDD to BDD. That is to say instead of simply testing functionality that you have already coded, shift to describing the behaviour you would like to see, independent of implementation. To help with this mindset shift I recommend renaming the class from BasicCalculatorTest to BasicCalculatorSpecification.

The code below uses the three methods Given, When and Then with a string argument to describe the behaviour of the software in a particular scenario.

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MSTestContrib.Specifications;

namespace Calculator.Core.Specifications
{
  [TestClass]
  [SpecificationDescription("As a user I want to perform mathematical calculations so my head doesn't hurt.")]
  public class BasicCalculatorSpecification : Specification
  {
    [TestMethod]
    [ScenarioDescription("Add two to a calculator with zero on the accumulator.")]
    public void AddTwoToACalculatorWithZeroOnTheAccumulator()
    {
      Given("a calculator with zero on the accumulator")
        .When("two is added to the accumulator")
        .Then("the accumulator should show two");
    }
  }
}

Step 5: Inconclusive Test/Scenario

Once you have gotten this far you are able to run the test, but because there is no implementation it will fail with an inconclusive result.

148324

Step 6: Implementing Test/Scenario

The Given/When/Then methods each support the ability to provide an expression which executes the code which matches the adjacent statement. Also note in this case I defined a variable to hold the calculator after it is instansiated in the Given method's expression.

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MSTestContrib.Specifications;

namespace Calculator.Core.Specifications
{
  [TestClass]
  [SpecificationDescription("As a user I want to perform mathematical calculations so my head doesn't hurt.")]
  public class BasicCalculatorSpecification : Specification
  {
    public BasicCalculator Calculator { get; private set; }

    [TestMethod]
    [ScenarioDescription("Add two to a calculator with zero on the accumulator.")]
    public void AddTwoToACalculatorWithZeroOnTheAccumulator()
    {
      Given("a calculator with zero on the accumulator", x => Calculator = new BasicCalculator())
        .When("two is added to the accumulator", x => Calculator.Add(2))
        .Then("the accumulator should show two", x => Calculator.Accumulator == 2);
    }
  }
}

Step 7: Successful Test

Thats it! We now have a successfully passing test.

148330

Last edited Sep 5, 2010 at 1:17 PM by MitchDenny, version 20