Fluent, Ubiquitous Assertions with xUnit

An entry about tdd | c# 3.0 Publication date 28. January 2009 13:03

With most testing frameworks, you write assertions like this:

var item = new object();

var list = new List<object> {item};

 

Assert.Equal(1, list.Count);

NUnit introduced a constraint-based assertion model a while back, which lets you write the above assertion in a more fluent manner:

Assert.That(list.Count, Is.EqualTo(1));

While this syntax certainly makes for more naturally readable assertions (“assert that List.Count is equal to 1”), it is quite verbose, awkward and has a less discoverable API (you have to know about all the IConstraints available).

With the extension method feature in C# 3.0, I’ve seen several experienced TDDers begin to use their own custom fluent assertion interfaces, resulting in syntax like this:

list.Count.ShouldBeEqualTo(1);

I’ve been meaning to write a set of extension methods myself to use with xUnit, and I’ve finally found the time to do so:

public static class AssertionHelpers
{
    /// <summary>
    /// Verifies that the string contains a given substring, using the current culture
    /// </summary>
    /// <param name="actualString"></param>
    /// <param name="expectedSubstring">The sub-string expected to be in the string</param>
    public static void ShouldContain(this string actualString, string expectedSubstring)
    {
        Assert.Contains(expectedSubstring, actualString);
    }

    /// <summary>
    /// Verifies that the string contains a given substring, using the given comparison type
    /// </summary>
    /// <param name="actualString"></param>
    /// <param name="expectedSubstring">The sub-string expected to be in the string</param>
    /// <param name="comparisonType">The type of string comparison to perform</param>
    public static void ShouldContain(this string actualString, string expectedSubstring, StringComparison comparisonType)
    {
        Assert.Contains(expectedSubstring, actualString, comparisonType);
    }

    /// <summary>
    /// Verifies that the collection contains a given object
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="collection"></param>
    /// <param name="expected">The object expected to be in the collection</param>
    public static void ShouldContain<T>(this IEnumerable<T> collection, T expected)
    {
        Assert.Contains(expected, collection);
    }

    /// <summary>
    /// Verifies that the collection contains a given object, using the given comparer
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="collection"></param>
    /// <param name="expected">The object expected to be in the collection</param>
    /// <param name="comparer">The comparer to use</param>
    public static void ShouldContain<T>(this IEnumerable<T> collection, T expected, IComparer<T> comparer)
    {
        Assert.Contains(expected, collection, comparer);
    }

    /// <summary>
    /// Verifies that the string does not contain a given substring, using the current culture
    /// </summary>
    /// <param name="actualString"></param>
    /// <param name="expectedSubstring"></param>
    public static void ShouldNotContain(this string actualString, string expectedSubstring)
    {
        Assert.DoesNotContain(expectedSubstring, actualString);
    }

    /// <summary>
    /// Verifies that the string does not contain a given substring, using the given comparison type
    /// </summary>
    /// <param name="actualString"></param>
    /// <param name="expectedSubstring"></param>
    /// <param name="comparisonType"></param>
    public static void ShouldNotContain(this string actualString, string expectedSubstring, StringComparison comparisonType)
    {
        Assert.DoesNotContain(expectedSubstring, actualString, comparisonType);
    }

    /// <summary>
    /// Verifies that the collection does not contain a given object
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="collection"></param>
    /// <param name="expected"></param>
    public static void ShouldNotContain<T>(this IEnumerable<T> collection, T expected)
    {
        Assert.DoesNotContain(expected, collection);
    }

    /// <summary>
    /// Verifies that the collection does not contain a given object, using the given comparer
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="collection"></param>
    /// <param name="expected"></param>
    /// <param name="comparer"></param>
    public static void ShouldNotContain<T>(this IEnumerable<T> collection, T expected, IComparer<T> comparer)
    {
        Assert.DoesNotContain(expected, collection, comparer);
    }

    /// <summary>
    /// Verifies that the collection is empty
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="collection"></param>
    public static void ShouldBeEmpty<T>(this IEnumerable<T> collection)
    {
        Assert.Empty(collection);
    }

    /// <summary>
    /// Verifies that the objects is equal to the given object, using a default comparer
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="actual"></param>
    /// <param name="expected"></param>
    public static void ShouldBeEqualTo<T>(this T actual, T expected)
    {
        Assert.Equal(actual, expected);
    }

    /// <summary>
    /// Verifies that the object is equal to the given object, using a custom comparer
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="actual"></param>
    /// <param name="expected"></param>
    /// <param name="comparer"></param>
    public static void ShouldBeEqualTo<T>(this T actual, T expected, IComparer<T> comparer)
    {
        Assert.Equal(actual, expected, comparer);
    }

    /// <summary>
    /// Verifies that the condition is false
    /// </summary>
    /// <param name="condition"></param>
    public static void ShouldBeFalse(this bool condition)
    {
        Assert.False(condition);
    }

    /// <summary>
    /// Verifies that the object is in within a given range
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="actual"></param>
    /// <param name="low">The inclusive low value</param>
    /// <param name="high">The inclusive high value</param>
    public static void ShouldBeInRange<T>(this T actual, T low, T high)
    {
        Assert.InRange(actual, low, high);
    }

    /// <summary>
    /// Verifies that the object is within a given range, using a custom comparer
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="actual"></param>
    /// <param name="low">The inclusive low value</param>
    /// <param name="high">The inclusive high value</param>
    /// <param name="comparer">The custom comparer to use</param>
    public static void ShouldBeInRange<T>(this T actual, T low, T high, IComparer<T> comparer)
    {
        Assert.InRange(actual, low, high, comparer);
    }

    /// <summary>
    /// Verifies that the object is assignable from the given type
    /// </summary>
    /// <param name="object"></param>
    /// <param name="expectedType">The type the object should be</param>
    public static void ShouldBeAssignableFromType(this object @object, Type expectedType)
    {
        Assert.IsAssignableFrom(expectedType, @object);
    }

    /// <summary>
    /// Verifies that the object is not of the given type
    /// </summary>
    /// <param name="object"></param>
    /// <param name="expectedType">The type the object should not be</param>
    public static void ShouldNotBeOfType(this object @object, Type expectedType)
    {
        Assert.IsNotType(expectedType, @object);
    }

    /// <summary>
    /// Verifies that the object is of the given type
    /// </summary>
    /// <param name="object"></param>
    /// <param name="expectedType">The type the object should be</param>
    public static void ShouldBeOfType(this object @object, Type expectedType)
    {
        Assert.IsType(expectedType, @object);
    }

    /// <summary>
    /// Verifies that the collection is not empty
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="collection"></param>
    public static void ShouldNotBeEmpty<T>(this IEnumerable<T> collection)
    {
        Assert.NotEmpty(collection);
    }

    /// <summary>
    /// Verifies that the object is not equal to a given object, using a default comparer
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="actual"></param>
    /// <param name="expected"></param>
    public static void ShouldNotBeEqualTo<T>(this T actual, T expected)
    {
        Assert.NotEqual(expected, actual);
    }

    /// <summary>
    /// Verifies that the object is not equal to a given object, using a custom comparer
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="actual"></param>
    /// <param name="expected"></param>
    /// <param name="comparer"></param>
    public static void ShouldNotBeEqualTo<T>(this T actual, T expected, IComparer<T> comparer)
    {
        Assert.NotEqual(expected, actual, comparer);
    }

    /// <summary>
    /// Verifies that the object is not in the given range
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="actual"></param>
    /// <param name="low">The inclusive low value</param>
    /// <param name="high">The inclusive high value</param>
    public static void ShouldNotBeInRange<T>(this T actual, T low, T high)
    {
        Assert.NotInRange(actual, low, high);
    }

    /// <summary>
    /// Verifies that the object is not in the given range, using a custom comparer
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="actual"></param>
    /// <param name="low">The inclusive low value</param>
    /// <param name="high">The inclusive high value</param>
    /// <param name="comparer">The custom comparer</param>
    public static void ShouldNotBeInRange<T>(this T actual, T low, T high, IComparer<T> comparer)
    {
        Assert.NotInRange(actual, low, high, comparer);
    }

    /// <summary>
    /// Verifies that the object is not null
    /// </summary>
    /// <param name="object"></param>
    public static void ShouldNotBeNull(object @object)
    {
        Assert.NotNull(@object);
    }

    /// <summary>
    /// Verifies that the object is not the same as the given object
    /// </summary>
    /// <param name="actual"></param>
    /// <param name="expected"></param>
    public static void ShouldNotBeSameAs(this object actual, object expected)
    {
        Assert.NotSame(expected, actual);
    }

    /// <summary>
    /// Verifies that the object is null
    /// </summary>
    /// <param name="object"></param>
    public static void ShouldBeNull(this object @object)
    {
        Assert.Null(@object);
    }

    /// <summary>
    /// Verifies that the object is the same as the given object
    /// </summary>
    /// <param name="actual"></param>
    /// <param name="expected"></param>
    public static void ShouldBeSameAs(this object actual, object expected)
    {
        Assert.Same(expected, actual);
    }

    /// <summary>
    /// Verifies that the condition is true
    /// </summary>
    /// <param name="condition"></param>
    public static void ShouldBeTrue(this bool condition)
    {
        Assert.True(condition);
    }

    /// <summary>
    /// Verifies that the delegate throws the given exception
    /// </summary>
    /// <typeparam name="EXCEPTION"></typeparam>
    /// <param name="action"></param>
    public static void ShouldThrow<EXCEPTION>(this Action action) 
        where EXCEPTION : Exception
    {
        Assert.Throws<EXCEPTION>(new Assert.ThrowsDelegate(action));
    }

    /// <summary>
    /// Verifies that the delegate does not throw any exceptions
    /// </summary>
    /// <param name="action"></param>
    public static void ShouldNotThrow(this Action action)
    {
        Assert.DoesNotThrow(new Assert.ThrowsDelegate(action));
    }
}

/// <summary>
/// Static gateway for building actions fluently
/// </summary>
public static class The
{
    public static Action Action(Action action)
    {
        return action;
    }
}
}

So far, I’ve created extension methods which wrap all of xUnits Assert methods, but I can certainly imagine other more (domain) specific Should* methods which would be helpful to have as well. Not everybody agrees, though.

Ubiquitous Assertion Syntax

The step from here towards an ubiquitous assertion syntax, as described by Jay Fields, is to create a set of complementary extension methods for behavior based assertions (mock expectations). This would have to involve some lambda trickery, perhaps similar to the ShouldThrow trick I picked up from JP’s blog. More on that later, perhaps.

Currently rated 5.0 by 4 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Comments

Powered by BlogEngine.NET 1.4.5.0

Welcome!

My name is Fredrik Kalseth, and this is my blog - thanks for visiting! I am fortunate enough to work with what I love for a living, and this blog is essentially the biproduct of that.

I work as a senior consultant for Capgemini, and am also an active participant in the Norwegian .NET community, as an avid attendee but also as a speaker (most recently at NNUG and MSDN Live).

As a developer, I have a wide circle of interest. My primary passion is for agile, test-driven development, with focus on best practices and clean code. That said, I also love to work on the frontend, especially with web development.

On Twitter? My handle is fkalseth. On LinkedIn? I`m there too.

Disclaimer

This is a personal blog; any opinions expressed here are my own and do not necessarily reflect those of my employer. All content herein is my own original creation, and as such is protected by copyright law. Unless otherwise stated, all source code posted on this blog is freely usable under the Microsoft Permissive License.

What Readers Talk About

Comment RSS