DEV Community

Cover image for Sparky's Tool Tips: Fluent Assertions
Brian Schroer
Brian Schroer

Posted on • Edited on

Sparky's Tool Tips: 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.

Fluent Assertions logo

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);
Enter fullscreen mode Exit fullscreen mode

...can be fluently expressed as:

using FluentAssertions;
. . .
daysInJune.Should().Be(31);
Enter fullscreen mode Exit fullscreen mode

"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");
Enter fullscreen mode Exit fullscreen mode

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");
Enter fullscreen mode Exit fullscreen mode

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

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

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

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);
Enter fullscreen mode Exit fullscreen mode

...the failure message is:

Assert.AreEqual failed. Expected:<Brian>. Actual:<Bryan>.
Enter fullscreen mode Exit fullscreen mode

With Fluent Assertions:

string correctNameSpelling = "Bryan";
correctNameSpelling.Should().Be("Brian");
Enter fullscreen mode Exit fullscreen mode

..the failure message is:

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

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!

Top comments (0)