I originally posted an extended version of this post on my blog a couple of weeks ago. It's part of a series I've been publishing, called Unit Testing 101.
Last time, we went through some best practices to write better assertions on our tests. This time, let's focus on how to use custom assertions in our tests.
Use custom assertions to encapsulate multiple assertions on a single method and to express assertions in the same language as the domain model. Write custom assertions with local methods or extension methods on the result object of the tested method or on the fakes objects.
We can either create custom assertions on top of MSTest Assert
class. And, our own Verify()
methods on Moq mocks.
Custom MSTest Assert methods
To write custom assertions with MSTest:
- Write an extension method on top of the Assert class
- Compare the expected and actual parameters
- Throw an AssertFailedException if the comparison fails
Here, we're creating a StringIsEmpty()
method.
internal static class CustomAssert
{
public static void StringIsEmpty(this Assert assert, string actual)
{
if (string.IsNullOrEmpty(actual))
return;
throw new AssertFailedException($"Expect empty string but was {actual}");
}
}
Then, we can use StringIsEmpty()
with the That
property. Like this,
Assert.That.StringIsEmpty("");
With this new assertion method, we can refactor one of our tests for Stringie, a (fictional) library to manipulate strings. We used Stringie to learn 4 common mistakes when writing tests and 4 test naming conventions.
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Stringie.UnitTests;
[TestClass]
public class RemoveTests
{
[TestMethod]
public void Remove_NoParameters_ReturnsEmpty()
{
string str = "Hello, world!";
string transformed = str.Remove();
Assert.That.StringIsEmpty(transformed); // 👈
}
}
With our custom StringIsEmpty()
method, our test is more readable. And, we wrote the tests in the same terms as our domain language.
Even better, since our StringIsEmpty()
method deals with strings, we can use the StringAssert
class as parameter in our custom method and rename it to IsEmpty()
.
Voilà! That's how to write custom assertions with MSTest. With custom assertions we have our tests written in the same terms as our domain model.
Remember, you can simply write private methods grouping your regular assertions and share them in a base test class. Or write extensions methods on the output of the method being tested. That would make your test even easier to read.
Upgrade your unit testing skills with my course: Mastering C# Unit Testing with Real-world Examples on my Gumroad page. Practice with hands-on exercises and learn best practices by refactoring real-world unit tests.
Happy testing!
Top comments (2)
Very good post. I remain with one question: how can I "force" the Visual Studio TestExplorer to recognize methods, that are being tested inside a custom assertion method? I want to see a green tick and the number of test on each method that I have tested...
Well, I've used custom assertion methods inside the regular test methods (annotated with
[TestMethod]
for MSTest) and I've never needed extra changes to make the test appear on the Test Explorer.With our custom assertions, we only replace the last A (Assert) of the AAA principle, not the whole test method.