Fluent Assertions 10 Top Tips
Tags: webdev, opensource, csharp, testing
Published: 2021-06-22 11:29:09

<h2><strong>Introduction</strong></h2>
<p>The value of writing unit tests is well understood these days and there are some really useful frameworks and tools which developers depend upon.</p>
<p>One such library is <a href="https://fluentassertions.com/" target="_blank" rel="noopener nofollow">Fluent Assertions</a>.&nbsp; It is an open source library that integrates with several Microsoft platforms such as .NET or .NET Core, and supports several testing frameworks including MS Test, NUnit and XUnit.&nbsp; The main motivation for its development is to show clear error messages returned by failed tests. Fluent Assertions has many extension methods which can improve the efficiency and readability of unit test code.&nbsp; The project allows for descriptive outcomes which suits TDD and BDD testing methodologies, but it is useful even if unit tests are written after the main code!</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://ivwebsitestorprod.blob.core.windows.net/iv-website-files/greentesttubes_637599504912048371.jpg" alt="" width="441" height="294" /></p>
<p>For our <a href="https://www.innovensa.co.uk/services/custom-software-development">backend development</a>, we <a href="https://www.innovensa.co.uk/about/technology-we-use">code mostly in C#</a> and use XUnit as our testing framework. We introduced Fluent Assertions on a project a couple of years ago to help make our assertions cleaner as we particularly liked the concept of Assertion Scopes (more on that later). &nbsp;Since then, we&rsquo;ve continued to use Fluent Assertions, but we have used it in a limited way using the same set of features.</p>
<p>However, recently I felt it was time for a knowledge refresh of Fluent Assertions and it has revealed plenty of opportunities for us to improve our unit test code.</p>
<p>This article covers my ten top tips for using Fluent Assertions from this recap.&nbsp; If you are using this library in your tests, then hopefully there&rsquo;s a tip or two that helps you out.&nbsp; This article covers the features that have jumped out at me, but there are many more useful features in the library.</p>
<p>It is assumed that you have a basic knowledge of Fluent Assertions or are actively using it. If you don&rsquo;t, fear not, you can always skim through the <a href="https://fluentassertions.com/introduction" target="_blank" rel="noopener nofollow">docs</a> later!</p>
<p>&nbsp;</p>
<h2><strong>10 Top Fluent Assertion Tips</strong></h2>
<p>So, here are my ten top tips that I will keep in mind from now on when writing unit tests.</p>
<p><em>At the time of writing Fluent Assertion v5.10.3 was used.</em></p>
<p>&nbsp;</p>
<h3>1. Use object graph comparison</h3>
<p>Some of the early test code we wrote was comparing object graphs and we were writing out property to property comparisons.&nbsp; As you can imagine, this was quite tedious!</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://ivwebsitestorprod.blob.core.windows.net/iv-website-files/fa-code-1_637598919630980626.png" alt="C# unit test code" width="587" height="142" /></p>
<p>Fluent Assertions has a really useful extension method <code>BeEquivalentTo()</code>.&nbsp;&nbsp; This allows object graph comparison and therefore removes the need to list all properties for comparison.&nbsp; This reduces the time required to write the test code and there is less of it to read and maintain.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://ivwebsitestorprod.blob.core.windows.net/iv-website-files/fa-code-2_637598919631934920.png" alt="Use of BeEquivalentTo() method" width="695" height="36" /></p>
<p><code>BeEquivalentTo()</code> is very powerful and has plenty of options.&nbsp; Among its features, you can select properties and/or fields to include or exclude, specify collection order, and it has a diagnostics mode to help debug deep into object graphs.</p>
<p>&nbsp;</p>
<h3>2. Use Date formatting extension methods</h3>
<p>There are a set of date formatting extension methods which allow for dates and times to be written more expressively.&nbsp; This approach provides clarity which is useful for non-developers to understand the test code.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://ivwebsitestorprod.blob.core.windows.net/iv-website-files/fa-code-3_637598921884336881.png" alt="Date formatting extension method usage" width="432" height="30" /></p>
<p>&nbsp;</p>
<h3>3. Use DateTime and TimeSpan assertions</h3>
<p>There are lots of useful assertions for dates and times and using them helps to make the code more readable.&nbsp; Use these extensions to replace the traditional code comparison operators.&nbsp;</p>
<p>One other point to mention, a date can be checked that it falls within a certain timeframe.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://ivwebsitestorprod.blob.core.windows.net/iv-website-files/fa-code-4_637598921885745818.png" alt="Date extension method" width="865" height="30" /></p>
<h2>&nbsp;</h2>
<h3>4. Use Collection extension methods</h3>
<p>Beyond the typical collection assertion methods you would expect to find, such as checking the count, there are some powerful methods to perform assertions on collections and the items they contain.&nbsp; These include asserting the order of the items, checking for uniqueness and whether a collection is a subset of another collection.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://ivwebsitestorprod.blob.core.windows.net/iv-website-files/fa-code-5_637598921885985418.png" alt="Collection extension methods" width="492" height="158" /></p>
<p>Other powerful methods include <code>Contain</code> and <code>ContainSingle</code>, these help validate that at least one or exactly one item in the collection meets a criteria.</p>
<p>Finally, <code>SatisfyRespectively</code> is a method which allows you to perform complex assertions on each item in a collection.</p>
<p>&nbsp;</p>
<h3>5. Use Which</h3>
<p>The <code>Which</code> extension allows for another assertion to be chained onto a previous assertion that identifies a single item in a collection.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://ivwebsitestorprod.blob.core.windows.net/iv-website-files/fa-code-6_637599466682101576.png" alt="Use of Which() extension method" width="680" height="49" /></p>
<h2>&nbsp;</h2>
<h3>6. Use Exception Assertions</h3>
<p>In our code we are using the exception assertions for top-level exception type checking.&nbsp; However, there is more that can be done! Using <code>WithMessage</code> allows us to check the message content is as expected.&nbsp; Also, it is possible to check the inner exception and the individual properties of the exception.</p>
<p>&nbsp;</p>
<h3>7. Use Perform Assembly Assertions</h3>
<p>In many of the systems we develop we use a layered architecture, specifically the <a href="https://jeffreypalermo.com/2008/07/the-onion-architecture-part-1/" target="_blank" rel="noopener nofollow">Onion Architecture</a>. This architecture states that code can only reference immediate inner layers. Fluent Assertions provides a pair of useful assertions to check that this rule is enforced at the assembly level.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://ivwebsitestorprod.blob.core.windows.net/iv-website-files/fa-code-7_637599466683783725.png" alt="Use of Reference() and NotReference() extension methods" width="520" height="122" /></p>
<p>&nbsp;</p>
<h3>8. Improve Should().BeTrue() code</h3>
<p>Ok, so this is actually a tip from the Fluent Assertions documentation itself!&nbsp; If the code is not evaluating a Boolean directly, then it is likely that <code>Should().BeTrue()</code> assertions can be written using a more expressive assertion syntax.&nbsp; This will improve readability and the error message displayed. There are myriad examples in the <a href="https://fluentassertions.com/tips/#improved-assertions" target="_blank" rel="noopener nofollow">documentation</a> to check out.</p>
<p>&nbsp;</p>
<h3>9. Use AssertionScope</h3>
<p>This is something that we already use and it is quite a fundamental feature, but it is worthwhile mentioning. &nbsp;By using the <code>using (new AssertionScope()) { }</code> code block, all assertions are evaluated and if the test fails, the error will include all messages from multiple failed assertions.</p>
<p>&nbsp;</p>
<h3>10. Other points</h3>
<p>Finally, a couple of other things were noticed when reading through the docs.&nbsp; Neither of these features we currently need to use, but they are now kept in mind:</p>
<ul>
<li>Can perform execution time assertions to check code performance.</li>
<li>Can perform XML assertions on LINQ-to-XML classes.</li>
</ul>
<p>&nbsp;</p>
<h2><strong>Conclusion</strong></h2>
<p>We like Fluent Assertions for the benefits of clear error messages, more readable test code and that fewer lines of code are needed for our tests.&nbsp; We are pleased to have refreshed our knowledge in this area and we can improve our future tests as well as refactor older ones.</p>
<p>This exercise taught us an important lesson in general.&nbsp; The world of software development is ever-changing and it pays dividends to keep up to date with existing tools.</p>
<p>Refreshing your knowledge of the libraries, tools and frameworks you use will provide valuable benefits to you, your code and those you work for.</p>