🎵These are a few of my favorite tools: Fluent Assertions

I like writing unit tests more than the average developer (not saying much, I realize - Testing is something many devs... de-test 🥁).

I really appreciate tools that make it easier and more fun to write tests (in fact, I've written some myself), and Fluent Assertions is one of my favorites.

The Microsoft.VisualStudio.TestTools.UnitTesting Assert, StringAssert and CollectionAssert classes are... OK, I guess. (It's been a while since I used NUnit and I've never used xUnit, so I don't know about the built-in asserts in those frameworks.)

Fluent Assertions is a set of .NET extension methods that allow you to specify the expected outcome of a TDD or BDD-style unit test with a sentence-like syntax. For example, the "MSTest" syntax:

Assert.AreEqual(31, daysInJune);

...can be fluently expressed as:

using FluentAssertions;
. . .
daysInJune.Should().Be(31);

"Using" the "FluentAssertions" namespace enables a ton of useful "Should()" extension methods for:

strings:

  • Be(expected)
  • BeNull()
  • BeEmpty()
  • StartWith(expected)
  • EndWith(expected)
  • HaveLength(expected)
  • Contain(expected)
  • ...

numbers:

  • Be(expected)
  • BeGreatorOrEqualTo(expected)
  • BeLessOrEqualTo(expected)
  • BeInRange(minimum, maximum)
  • BePositive()
  • ...

DateTime:

  • Be(1.March(2022).At(22, 15))
  • BeOnOrAfter(1.March(2010))
  • HaveDay(1)
  • HaveMonth(3)
  • HaveYear(2010)
  • HaveHour(22)
  • BeLessThan(10.Minutes()).Before(otherDatetime)
  • ...

collections:

  • HaveCount(expected)
  • HaveSameCount(otherCollection)
  • Contain(expected)
  • NotContain(expected)
  • OnlyContain(x => x.predicate)
  • ...

These are just a few examples. There are dozens more methods for these and many other types.

And... assertions are chainable!

string stateName = "OHIO";
stateName.Should().StartWith("O").And.EndWith("O").And.Contain("HI");

Exceptions

I don't like the MS Test [ExpectedException(type)] attribute. Among other shortcomings, it doesn't let you test the exception message.

With Fluent Assertions, exception testing looks like this:

subject.Invoking(x => x.Foo("Hello"))
    .Should().Throw<InvalidOperationException>()
    .WithMessage("Hello is not allowed at this moment");

...or with "arrange / act / assert" syntax:

Action action = () => x.Foo("Hello");

action
    .Should().Throw<InvalidOperationException>()
    .WithMessage("Hello is not allowed at this moment");

Extensibility

Fluent Assertions is nicely set up to allow you to write your own "Should" extension methods.

There are many NuGet packages containing additional extension methods, including my own SparkyTestHelpers.Moq.Fluent.

But wait, there's more!

Fluent Assertions doesn't just give you better syntax for writing assertions - it gives you much better messages when an assertion fails, for example:

With Assert.AreEqual:

string correctNameSpelling = "Bryan";
Assert.AreEqual("Brian", correctNameSpelling);

...the failure message is:

Assert.AreEqual failed. Expected:<Brian>. Actual:<Bryan>.

With Fluent Assertions:

string correctNameSpelling = "Bryan";
correctNameSpelling.Should().Be("Brian");

..the failure message is:

Expected correctNameSpelling to be "Brian", but "Bryan" differs near "yan" (index 2).

The "differs near" and index are a bit of overkill for strings this short, but pretty handy for long strings!

(BTW, this is not a trivial example. correctNameSpelling SHOULD BE "Brian" and if your parents named you "Bryan", you should call them and complain.😏)

I like Fluent Assertions a lot, and if you're writing .NET unit tests, I think you .Should() give it a try!

21