Over a million developers have joined DZone.

Improving Unit Testing with FluentAssertions

DZone's Guide to

Improving Unit Testing with FluentAssertions

· DevOps Zone
Free Resource

The Nexus Suite is uniquely architected for a DevOps native world and creates value early in the development pipeline, provides precise contextual controls at every phase, and accelerates DevOps innovation with automation you can trust. Read how in this ebook.

FluentAssertions is an alternative assertion library for unit tests, to use instead of the methods in Assert class that Microsoft provides. It has much better support for exceptions and some other stuff that improves readability and makes it easier to produce tests.

The coding of Kentor.AuthServices was a perfect opportunity for me to do some real TDD (Test Driven Development) again. I have long thought that the [ExpectedException] attribute that MsTest offers is not enough, so when Albin Sunnanbo suggested that I’d look at FluentAssertions I decided to try it.

Verifying Exceptions

FluentAssertions offers a ShouldThrow() extension method to the Action delegate type. It asserts that invoking a particular action will throw an exception.

// Code from https://github.com/KentorIT/authservices/blob/master/
// Kentor.AuthServices.Tests/Saml2ResponseTests.cs
Action a = () => Saml2Response.Read(response).GetClaims();
.WithMessage("The Saml2Response must be validated first.");

Compared to the [ExpectedException] attribute this offers much better control.

The Problem with [ExpectedException]

The problem with [ExpectedException] is that it lacks an important property: exactly specifying where the exception is expected. Consider a test that first grabs a suitable object to test with LINQ and then provokes an exception.

public void Test_Using_ExpectedException()
  var attributes = typeof(Brand).GetMember("Nmae").Single()
  // This should throw.

When run, this test will be flagged green, despite the typo in the GetMember call. The problem is that GetMember throws an InvalidOperationException which is exactly what is required for the method to pass. There is no way with [ExpectedException] to make sure that the exception is thrown at the right place. There is also no way way to test more than one thing (for example a post condition that an object is in a consistent state after a method threw an exception).

Using FluentAssertions instead

With FluentAssertions it’s much better.

public void Test_Using_FluentAssertions()
    var attributes = typeof(Brand).GetMember("Nmae").Single()
    Action a = () => attributes.OfType<RequiredAttribute>().First()

The code to be tested for an exception is now placed in an action. The ShouldThrow() extension method executes the action and monitors for the exception. The test now properly fails because GetMember("Nmae").Single() throws an exception – which is not expected.

More FluentAssertions

The basic syntax of FluentAssertions is to use a bunch of extension methods that extend everything. This is another example from Kentor.AuthServices.

// From https://github.com/KentorIT/authservices/blob/master/
// Kentor.AuthServices.Tests/IdentityProviderTests.cs
var r = ip.CreateAuthenticateRequest();

Here I can use one line to test that the result of ToXElement() has a Destination attribute and that the value of the attribute is correct.

The best: ShouldBeEquivalentTo

The best part of FluentAssertions is ShouldBeEquivalentTo. Combined with anonymous types it’s a really powerful way to test a new method. Especially when doing test driven development this is great because I can compile and run the test before I even touch the live code.

var expected = new
  Id = "Saml2Response_Read_BasicParams",
  IssueInstant = new DateTime(2013, 01, 01, 0, 0, 0, DateTimeKind.Utc),
  Status = Saml2StatusCode.Requester,
  Issuer = (string)null

Unfortunately there is a flaw in the API (or I haven’t learnt to use it correctly). ShouldBeEquivalentTo only tests the properties that are present in the subject of the test and ignores any extra properties found in the expected object. I would have preferred to have it the other way around. That way I could introduce a new property by starting with adding it to the expected object in a test.

In real life however that’s not a huge problem – just add the new property with a return null; getter to the tested object and the test will fail. But if I can wish for something, I’d love if FluentAssertions was extended with this behaviour, at least as a configurable option.

Installation FluentAssertions

Getting started with FluentAssertions is extremely easy. Just add the FluentAssertions Nuget package to the project and then include a using FluentAssertions; in the test file.

Installing FluentAssertions is so simple because it is just another way to write the assertions of the test – it doesn’t change the test infrastructure. That means that the test runner (which might require separate installations) is not affected. That’s a big advantage as having to install separate test runners into Visual Studio for a new project is a real pain.

The DevOps Zone is brought to you in partnership with Sonatype Nexus.  See how the Nexus platform infuses precise open source component intelligence into the DevOps pipeline early, everywhere, and at scale. Read how in this ebook


Published at DZone with permission of Anders Abel, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.


Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.


{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}