BDD Specification Extensions


Update: this blog is no longer active. For new posts and RSS subscriptions, please go to http://saintgimp.org.

A few people have asked me for more details on the specification extension methods we use to make our BDD tests more readable.  As I mentioned previously, SpecUnit.net is a great library that has all kinds of useful extension methods, but it’s written to be used on top of xUnit.net.  If you want to use the technique in the MSTest framework, you’ll have to implement your own spec extension methods.

Fortunately, they’re trivial to write.  Here’s an example of what we wrote for object references, which actually covers the majority of tests since our observations are often just testing for equality (or not) and nullness (or not).

/// <summary>

/// Provides BDD-style assertations for object references.

/// </summary>

public static class ObjectSpecificationExtensions

{

    /// <summary>

    /// Verifies that the object reference is null.

    /// </summary>

    /// <param name=”actual”>The reference to verify.</param>

    public static void ShouldBeNull(this object actual)

    {

        Assert.IsNull(actual);

    }

 

    /// <summary>

    /// Verifies that the object reference is not null.

    /// </summary>

    /// <param name=”actual”>The reference to verify.</param>

    public static void ShouldNotBeNull(this object actual)

    {

        Assert.IsNotNull(actual);

    }

 

    /// <summary>

    /// Verifies that two objects are equal.

    /// </summary>

    /// <param name=”actual”>The actual object.</param>

    /// <param name=”expected”>The expected object.</param>

    public static void ShouldEqual(this object actual, object expected)

    {

        Assert.AreEqual(expected, actual);

    }

 

    /// <summary>

    /// Verifies that two objects are not equal.

    /// </summary>

    /// <param name=”actual”>The actual object.</param>

    /// <param name=”notExpected”>The unexpected object.</param>

    public static void ShouldNotEqual(this object actual, object notExpected)

    {

        Assert.AreNotEqual(notExpected, actual);

    }

 

    /// <summary>

    /// Verifies that two objects are the same instance.

    /// </summary>

    /// <param name=”actual”>The actual object.</param>

    /// <param name=”expected”>The expected object.</param>

    public static void ShouldBeTheSameAs(this object actual, object expected)

    {

        Assert.AreSame(expected, actual);

    }

 

    /// <summary>

    /// Verifies that two objects are not the same instance.

    /// </summary>

    /// <param name=”actual”>The actual object.</param>

    /// <param name=”notExpected”>The unexpected object.</param>

    public static void ShouldNotBeTheSameAs(this object actual, object notExpected)

    {

        Assert.AreNotSame(notExpected, actual);

    }

 

    /// <summary>

    /// Verifies that an object is of a specific type.

    /// </summary>

    /// <param name=”actual”>The actual object.</param>

    /// <param name=”expected”>The expected type.</param>

    public static void Is(this object actual, Type expected)

    {

        Assert.IsInstanceOfType(actual, expected);

    }

 

    /// <summary>

    /// Verifies that an object is not a specific type.

    /// </summary>

    /// <param name=”actual”>The actual object.</param>

    /// <param name=”expected”>The unexpected typ.</param>

    public static void IsNot(this object actual, Type notExpected)

    {

        Assert.IsNotInstanceOfType(actual, notExpected);

    }

}

We also have extensions for specific types (Int32, String, TimeSpan, Single, etc) that implement things like ShouldBeGreaterThan(), ShouldBeLessThan(), ShouldBeEmpty(), ShouldBeCloseTo(), etc.  The nice thing about this strategy is that we don’t have to have our entire spec extension library written up front.  If we’re writing a test and want to assert some condition that isn’t already implemented by a spec extension method, we can just add an appropriate extension method at that point.

The extension methods above are all a single line that just pass the call through to the MSTest Assert class, but spec extensions are even more helpful when you have a complex assertion that requires multiple lines of code to express.  You can hide all that complexity behind a nice intention-revealing extension method that leaves your tests crisp and easy to understand.

Comments (4)

  1. Thank you for submitting this cool story – Trackback from DotNetShoutout

  2. wekempf says:

    You might be interested in Specificity (http://www.codeplex.com/specificity) which does something similar, but without "polluting" intellisense when not asserting on a value. Instead of obj.ShouldNotBeNull() you do Specify.That(obj).ShouldNotBeNull().

  3. Eric Lee says:

    Thanks for the link.  I’ll have to try it out and see how it feels.  Seems like it would be more verbose than necessary in the tests; that is, the extra "Specify.That()" wouldn’t add any extra clarity to the way the code reads and arguably just clutters it.  But on the other hand, keeping Intellisense uncluttered is a big win.

  4. Tim Scott says:

    You might check out this:

    http://code.google.com/p/shouldit/

    ShouldIt is an open source library of fluent specification extensions.  Examples of the syntax are:

    obj.Should().Not.Be.Null();

    list.Should().Count.Exactly(2)

       .Should().Contain.One(x => x.FirstName == "Jim")

       .Should().Contain.One(x => x.FirstName == "Bill");

    myBool.Should().Be.False();

    It works with any testing framework with no configuration.