MSTest v2: Create new asserts

This post is part of the serie 'MSTest v2'. Be sure to check out the rest of the blog posts of the serie!

MSTest v2 is extensible. In a previous post, we saw how to extend data tests. In this post, we'll see how to create new asserts.

The MSTest framework contains lots of assert methods. They are all located in 3 classes: Assert, StringAssert, CollectionAssert. Sometimes, you want more asserts. In this case you can create your own assert methods.

An assert method is a method that validate a value statifies a specific criteria. If the criteria is not statisfied, an exception is thrown. In MSTest, the exception is of type AssertFailedException, but you can throw any kind of exception.

Let 's create a custom assert to ensure a string is equal to another one. If the 2 strings are differents the message contains a formatted diff that shows blank characters and the first different character:

internal static class MyAssert
{
    public static void StringEquals(string expected, string actual)
    {
        if (expected == actual)
            return;

        throw new AssertFailedException(GetMessage(expected, actual));
    }

    private static string GetMessage(string expected, string actual)
    {
        var expectedFormat = ReplaceInvisibleCharacters(expected);
        var actualFormat = ReplaceInvisibleCharacters(actual);

        // Get the index of the first different character
        var index = expectedFormat.Zip(actualFormat, (c1, c2) => c1 == c2).TakeWhile(b => b).Count();
        var caret = new string(' ', index) + "^";

        return $@"Strings are differents.
Expect: <{expectedFormat}>
Actual: <{actualFormat}>
         {caret}";
    }

    private static string ReplaceInvisibleCharacters(string value)
    {
        return value
            .Replace(' ', '·')
            .Replace('\t', '→')
            .Replace("\r", "\\r")
            .Replace("\n", "\\n");
    }
}

In case the values are differents, the output of the test run look like:

[TestMethod]
public void Test1()
{
    MyAssert.StringEquals("abc def", "abc  def");
}
Message: Strings are differents.
Expect: <abc·def>
Actual: <abc··def>
             ^

Ok, that great. But MSTest v2 provides a way to integrate your assert into the provided asserts. If you look at the intellisense, you may have notice the That property. The property has no member:

So, why the hell is this property? The idea is to provide a property that returns an instance of Assert. This allows you to create extension methods 😃 (of course, the same property exists in StringAssert and CollectionAssert)

So, you can rewrite the assert method as an extension method:

internal static class MyAssert
{
    public static void StringEquals(this Assert assert, string expected, string actual)
    {
        if (expected == actual)
            return;

        throw new AssertFailedException(GetMessage(expected, actual));
    }

    // ...
}

Now, you can use the extension method in the test:

[TestMethod]
public void Test1()
{
    Assert.That.StringEquals("abc def", "abc  def");
}

This syntax is more readable as the line is a valid English sentence.

Conclusion

Writing custom assert method allows to create more useful message, or to create business specific asserts. MSTest v2 provides a nice way to create custom assert by using a extension method. The That property is very smart 😃

Leave a reply