Introduction
This article details how one can extend the existing unit test assertions provided by Microsoft's Visual Studio unit test
framework (see Assert
and StringAssert
).
I first give some guideline for writing concise and expressive assertions, followed by exploring the limits of the current Assert classes,
resulting in a suggestion on how to overcome these limits.
Writing Expressive Assertions
The assertion classes available contain some specific methods to assert certain conditions. The challenging part in applying assertions is, to
- call the proper assert methods which report the expected and actual values
- provide a meningful assertion message
- overcome the temptation to format the message by repeating the values from 1. above
Item 1. ensures concise and meaningful assertions, item 2. makes potential additional code comments redundant, item 3. does not re-invent the wheel.
E.g., the following assertion should be rewritten:
if (list.Count != 6)
{
Assert.Fail("Error: list.Count = {0}", list.Count);
}
Resulting message: | Assert.Fail failed. Error: list.Count = 0 |
The first attempt replaces the if
-block:
Assert.IsTrue(list.Count == 6, "Error: list.Count = {0}", list.Count);
Resulting message: | Assert.IsTrue failed. Error: list.Count = 0 |
The next attempt uses an appropriate Assert method:
Assert.AreEqual(6, list.Count, "Error: list.Count = {0}", list.Count);
Resulting message: | Assert.AreEqual failed. Expected:<6>. Actual:<0>. Error: list.Count = 0 |
The final attempt makes the comment and the message formatting redundant:
Assert.AreEqual(6, list.Count, "wrong number of xxx");
Resulting message: | Assert.AreEqual failed. Expected:<6>. Actual:<0>. wrong number of xxx |
This last assertion is the desired one: appropriate method used that reports the values, a concise non-formatted message, no useless comment. Keep it Simple!
Missing Assert Methods
So, how asserting the following:
There are no such function like Assert.Greater(...)
. The closest we get to the guideline above is Assert.IsTrue(...)
. E.g.:
Assert.IsTrue(list.Count > 4, "too little xxx elements");
Resulting message: | Assert.IsTrue failed. too little xxx elements |
No word about expected and actual value, no word about the kind of check applied...
So, fall back to commenting and formatting messages? E.g. (not my recommendation):
Assert.IsTrue(list.Count > 4, "xxx list has only {0} elements instead of more than 4 elements", list.Count);
Resulting message: | Assert.IsTrue failed. xxx list has only 1 elements instead of more than 4 elements |
Suggested Solution
The attached code provides an ExtAssert
class that provides the four missing functions:
public static void Greater<T>(T a, T b, string message, params object[] args) where T : IComparable<T>
public static void GreaterOrEqual<T>(T a, T b, string message, params object[] args) where T : IComparable<T>
public static void LessOrEqual<T>(T a, T b, string message, params object[] args) where T : IComparable<T>
public static void Less<T>(T a, T b, string message, params object[] args) where T : IComparable<T>
With these, you can re-write the relation assertion, following the guidelines:
ExtAssert.Greater(list.Count, 4, "too little xxx elements");
Resulting message: | Assert.AreEqual failed. Expected:<(a > b)>. Actual:<(1 < 4)>. too little xxx elements |
Behind the Scenes
The concept is quite simple:
- define an "ok" string, e.g.
"(a > b)"
- provide a check function that returns the "ok" string if the check passes or the actual value relation if it fails, e.g.
"(a > b)"
versus "(1 < 4)"
- compare the outcome of the check with the "ok" string:
Assert.AreEqual(ok, check, ...)
This concept is implemented in the attached code for the case of relation assertions. One can extend assertion as indicated in the ExtAssert for the NullOrEmpty check, etc.
If you like the article, please rate it.
Any comments are welcome, e.g. if it is useful or suggestions for improvements, etc. Thanks!
History
- Version 1.0: 2012-01-29: Initial verision.
- Version 1.1: 2012-03-11: Added request for rating and for comments.