DevOps Zone is brought to you in partnership with:

I'm a software developer working as a senior consultant at Kentor in Stockholm, Sweden. My core competence is as a technical specialist within development and system architecture. In my heart I am, and probably will remain, a programmer. I still think programming is tremendously fun, more than 20 years after I first tried it. That's why my blog is named Passion for Coding.  Anders is a DZone MVB and is not an employee of DZone and has posted 74 posts at DZone. You can read more from them at their website. View Full User Profile

Improving Unit Testing with FluentAssertions

10.10.2013
| 1983 views |
  • submit to reddit

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();
 
a.ShouldThrow<InvalidOperationException>()
.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.

[TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
public void Test_Using_ExpectedException()
{
  var attributes = typeof(Brand).GetMember("Nmae").Single()
    .GetCustomAttributes(false);
 
  // This should throw.
  attributes.OfType<RequiredAttribute>().First()
    .FormatErrorMessage("InvalidName");
}

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.

[TestMethod]
public void Test_Using_FluentAssertions()
{
    var attributes = typeof(Brand).GetMember("Nmae").Single()
        .GetCustomAttributes(false);
 
    Action a = () => attributes.OfType<RequiredAttribute>().First()
        .FormatErrorMessage("InvalidName");
 
    a.ShouldThrow<InvalidOperationException>();
}

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();
 
r.ToXElement().Attribute("Destination").Should().NotBeNull()
   .And.Subject.Value.Should().Be(idpUri);

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
};
 
Saml2Response.Read(response).ShouldBeEquivalentTo(expected);

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.




Published at DZone with permission of Anders Abel, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)