Working with a legacy codebase using NUnit and .NET Framework, I’ve found that
there is a mix of NUnit assertions and assertions using the
Should library. This library is rather
old and, frankly, limited compared to
FluentAssertions. These newer two frameworks are
significantly more expressive, with APIs that cover myriad situations elegantly.
Questions in front of me:
- Are any of these libraries really worthwhile compared to simply using NUnit’s
built-in assertions - either traditional or Assert.That style?
- If using any independent framework, which is the best choice for this code
- If selecting Shouldly or FluentAssertions, ought we to upgrade the old
My conclusion: favor Shouldly. Upgrade old asserts opportunistically for
consistency, but no need to go out of the way.
Full source code for these experiments is at https://github.com/stephenfuqua/AssertionComparison.
Why Use a Separate Library
Some argue that the assertion library simply ought to be independent of the unit
test framework, allowing greater flexibility in switching between frameworks.
Switching unit test frameworks in a large legacy project sounds rather tedious,
so that alone is an insufficient reason.
One problem I’ve run into frequently in legacy code is people switching up the
expected and actual responses. In classic NUnit,
expected comes first. This
situation is better with the NUnit3
Assert.That style; however, the values are
still hidden away inside of the assertion method call.
Assert.AreEqual(expected, actual); // old style
Assert.That(actual, Is.EqualTo(expected)); // new style
When a coder reverses the traditional style, and you need to fix a broken test,
it can get a bit confusing to figure out what is going on (especially if the
variables are not so clearly named). In the course of writing this project,
while switching back and forth between styles, I realized I had made this
mistake myself - wanting to put the actual result first and then compare it to
If continuing to use NUnit3, this alone is a good reason to switch to the new
Constrain Model. The three fluent frameworks evaluated here address this by
putting the actual result at the beginning of the assertion statement:
actual.Should().Be(expected); // FluentAssertions, and Should in Fluent mode
actual.ShouldBe(expected); // Shouldly
actual.ShouldEqual(expected); // Should
Another problem is coming up with a meaningful message, which is especially
important if you have multiple asserts in the same unit test (many people frown
at that, and I frown right back unless the coders are prone to large numbers of
mistakes per function). Each of these frameworks reports failures differently.
Compare these results:
- NUnit Assert:
Assert.AreEqual(-1, sum); Assert.That(sum, Is.EqualTo(-1));
Expected: -1 But was: 0
Expected sum to be -1L, but found 0L.
Should.Core.Exceptions.EqualException : Assert.Equal() Failure Expected: -1
Shouldly.ShouldAssertException : sum should be -1L but was 0L
The latter three all provide more information than the first. Of these, I find
the FluentAssertion response to be the most elegant for its compactness and
Documentation and Richness
Compared to the other two bolt-on libraries, FluentAssertions clearly has the
best documentation. Detailed and rich with examples, it was easy for me to find
the right syntax for the job. It also clear that the library has extensive
support for value types, references types, collections, and exceptions.
Shouldly’s documentation seems to be a work-in-progress. I was unable to find
documentation of their exception handling syntax - I had to look for the
functions in the object browser.
Should’s documentation is brief but relatively complete given that it is a
smaller package. Looking at the repo, it also clear that the project hasn’t been
touched in many years. This could mean that it simply works - but it also means
that others have passed it by in the meantime.
To get a sense of the syntax richness, let’s look at exception handling. Aside:
In NUnit, I never use the
[ExpectedException] attribute as I prefer to have
the assert clearly visible in the method body.
NUnit Outer Exception
Assert.Throws<ArgumentNullException>(RunNullSeries); // old style
Assert.That(RunNullSeries, Throws.ArgumentNullException); // new style
Fluent Assertions Outer Exception
Should Outer Exception
Shouldly Outer Exception
There is not much difference between these. Fluent Assertions requires one extra
method call. This is a general philosophical difference: it wants you to call
Should() first every time, and then exposes the full API. What I like about
this is that it presents a more consistent looking interface, compared to
combining elements together (e.g.
ShouldBe, etc.) This might
just be a matter of style.
Inner Exception Handling
Both Fluent Assertions and Shoudly make it easy to also check on an inner
exception. So does the new NUnit3 Constraint Model. With the other two
frameworks, you’re left with catching and inspecting the exception.
NUnit3 Constraint Model Inner Exception
#### Fluent Assertions Inner Exception
Shouldly Inner Exception
Across thousands of tests, execution time for these asserts could certainly add
up. Here is one place where FluentAssertions is not as attractive. I wrote the
same tests in each framework and ran them many times. The results below are
representative of the typical results in repeated executions:
Yikes! What’s going on here? Let’s drill into the results a bit…
There is one test that makes up nearly 80% of execution time. And it is a
public void TempVariable()
var sum = AggregateCalculator.Sum(1, -1);
.Be(-1); // purposefully wrong
Running that one test repeatedly, by itself, I see similar results. When I run
multiple tests, that one test always seems to take the longest. Applying the
[Order] attribute to the another test, to force another one to run first, the
longest time shifts to that test. Thus it seems to be a bootstrapping thing
(reflection?) with FluentAssertions - the first test seems to take much longer
to execute. Subtract out the effect of that one test, and FluentAssertions
performs better than classical NUnit asserts but a little bit worse than the
It is also interesting to see that the Constraint Model of NUnit3 performs very
well. The most time-consuming assertion there is for the ArgumentNullException.
The execution time differences are troubling. This sample code base may too
small to read too much into it, but this is an important finding and something
to watch out for. Based on documentation and richness of syntax I would want to
use Fluent Assertions, but if the project has a large number of tests, the small
performance difference could add up to a meaningful increase in total execution
time. If time to fully evaluate is lacking, then I feel that my best choices are
to either (a) focus on Assert.That syntax or (b) upgrade Should to Shoudly and
perhaps make time to pitch in on improving the documentation. Leaving existing
Should tests in place would seem to be harmless since the performance is good.