MSTest v2: Create new asserts
This post is part of the series 'MSTest v2'. Be sure to check out the rest of the blog posts of the series!
- MSTest v2: Setup a test project and run tests
- MSTest v2: Exploring asserts
- MSTest v2: Data tests
- MSTest v2: Test lifecycle attributes
- MSTest v2: Create new asserts (this post)
- MSTest v2: Customize test execution
- MSTest v2: Execute tests in parallel
- MSTest v2: Testing against multiple frameworks
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 assertions. In this case, you can create your assert methods.
An assert method is a method that validates a value satisfies specific criteria. If the criteria are not satisfied, 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 different 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 different.
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 different, the output of the test run look like:
[TestMethod]
public void Test1()
{
MyAssert.StringEquals("abc def", "abc def");
}
Message: Strings are different.
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 noticed 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 methods allows us to create more useful messages, or to create business-specific asserts. MSTest v2 provides a nice way to create custom assert by using an extension method. The That
property is very smart 😃
Do you have a question or a suggestion about this post? Contact me!