Using Fluent Assertions With C# And XUnit

by Kenji Elzerman
Unit Testing With Fluent Assertions - Kens Learning Curve

Writing unit tests isn’t hard, but the assertions can be a bit of a menace when it comes to reading them. Each assertion has a specific task (True, False, Equal, etc.). To make it a bit easier we can use a technique that is called fluent. The idea behind fluent is that you can chain methods with ease in a way the methods you use create a sentence that is easy to read. In this article, I will show you how using Fluent Assertions with C# can really help you create better unit tests.

Goals

The goal of this article is to show you the idea and working of Fluent Assertions, a library that can be obtained through NuGet.

After this article you:

  • Know what Fluent Assertions is
  • Understand the way Fluent Assertions works
  • Know how to chain different Fluent Assertions extension methods
  • Can create smaller unit tests by using Fluent Assertions.

Fluent Assertions In Short

Chaining methods/functions was – or still is – a popular way of combining functionality in one line of code. LINQ is an example of chaining those methods.

myMovies.GetAll().Where(x => x.Title.Contains("sh")).OrderBy(x => x.Title).First();

This is also called fluent: Making the methods make sense. If you read the line of code out loud it doesn’t make a lot of sense. Combining the chaining and the idea of fluent, adding assertions makes Fluent Assertions.

Fluent Assertions is a library that contains a lot of extension methods that can be changed together. This way you can make one line of code with different extensions. It’s not a new technique, but it’s very effective if done right.

Besides making the assertions easier to read, Fluent Assertions also brings a few unique features. Using Fluent Assertions with C# isn’t that hard. It’s pretty straightforward.

Using Fluent Assertions With C#

Before we can use these methods the library brings, we need to install a NuGet package into the test project.

Install-Package FluentAssertions

Let’s take a simple test case I have used in the article Testing Exceptions With XUnit.

[Theory]
[InlineData(0)]
[InlineData(-37894)]
public void Should_ThrowArgumentException_When_IdIsInvalid(int id)
{
    MyMovies myMovies = new MyMovies();

    Action act = () => myMovies.Get(id);

    ArgumentException result = Assert.Throws<ArgumentException>(act);

    Assert.NotNull(result);
    Assert.NotNull(result.Message);
    Assert.Equal("Id cannot be zero or less.", result.Message);
}

This test case tests if the myMovies.Get() throws an exception when the variable id is 0 or less. There are three assertions to check if the variable result is not null, if the result.message is not null, and if the message of the result equals the expected text.

We can reduce the number of assertions to one, but simply using Fluent Assertions.

[Theory]
[InlineData(0)]
[InlineData(-37894)]
public void Should_ThrowArgumentException_When_IdIsInvalid(int id)
{
    MyMovies myMovies = new MyMovies();

    Action act = () => myMovies.Get(id);

    ArgumentException result = Assert.Throws<ArgumentException>(act);

    result.Should().NotBeNull();
    result.Message.Should().NotBeNull().And.Be("Id cannot be zero or less.");
}

Apart from the fact the type Assert is no longer in the code, the whole assertion is easier to read. Try reading out loud lines 12 and 13. It makes sense, right?

That’s it? Nah! There is much more!

A Bigger Example

Here is a method where I use more Fluent Assertions methods.

[Fact]
public void Should_ShowTheAwesomeFluentAssertions()
{
    string myString = "Hello people! Welcome to my website Kens Learning Curve!";

    myString.Should()
        .NotBeNull()
        .And.NotBeEmpty()
        .And.Be("Hello people! Welcome to my website Kens Learning Curve!")
        .And.Contain("people")
        .And.MatchRegex("p.+le");
}

There is one string and there are 5 assertions in one line of code, and easy to read too. But the purpose of this example is to show you what is possible:

First I check if the myString is not NULL, then I check if the variable is not empty. The Be method checks if the value of the parameter is the same as the myString. Contain does what you would expect: Does the myString contain people? The last one is a regular expression that checks if the pattern matches the value of myString.

The possibilities are endless. The number of methods you can use is just too many to mention them all. Luckily, there is a lot of documentation on Fluent Assertions.

Using The Type

Let’s look back at the first example of Fluent Assertions I gave you:

[Theory]
[InlineData(0)]
[InlineData(-37894)]
public void Should_ThrowArgumentException_When_IdIsInvalid(int id)
{
    MyMovies myMovies = new MyMovies();

    Action act = () => myMovies.Get(id);

    ArgumentException result = Assert.Throws<ArgumentException>(act);

    result.Should().NotBeNull();
    result.Message.Should().NotBeNull().And.Be("Id cannot be zero or less.");
}

So, why can’t we make it one assertion? We are already checking if the result is not null. Why not continue chaining? Because Fluent Assertions has no idea what the properties of the result are.

Each extension method of Fluent Assertions returns an AndConstraint type, which has nothing to do with the variables you are trying to assert. But there is a way to fix this.

If you tell Fluent Assertions which type you are using (or expecting), it will return that particular type and then you know the properties. You can do this with the extension method BeOfType<>.

result.Should().NotBeNull().And.BeOfType<ArgumentException>().Which.Message.Should().Be("Id cannot be zero or less.");

The NotBeNull returns an AndConstraint. But the BeOfType<ArgumentException> returns the type Movie, allowing us to look into the properties of ArgumentException and grab the content of those properties.

Better Exception Testing

The way I used to test exceptions was rather cumbersome. Fluent Assertions make it a bit easier to write and test exception tests.

The previous code examples were about exception testing. After I showed you the BeOfType extension, this code example has 4 lines of code. With Fluent Assertions I can bring this back to 2 lines of code and it still makes sense.

The extension method Invoking will execute code in a safe environment. Meaning, that when an exception is thrown, the unit test will not crash. The result (exception or not) will be stored in a variable and you can continue building and testing on the outcome.

The extension method Throw<> can check if the outcome of the Invoking is an exception and if it’s of a specific exception type. After that, we can continue checking if the message thrown by the exception is correct.

The code looks like this:

[Theory]
[InlineData(0)]
[InlineData(-37894)]
public void Should_ThrowArgumentException_When_IdIsInvalid(int id)
{
    MyMovies myMovies = new();
    myMovies.Invoking(x => x.Get(id)).Should().Throw<ArgumentException>().Which.Message.Should().Be("Id cannot be zero or less.");
}

Cool, huh?!

Working With Collections

One of the things I hate doing while unit testing is asserting collections. It’s always a hassle to make sure the collection is correct. This usually ends in big and a lot of assertions.

Fluent Assertions makes it a little bit better. There are extensions that can help you assert collections. You can check if the list items are the same, check if the collection only has unique items, make sure the collection starts with a specific value, and so on.

Let’s take a look at the following code:

[Fact]
public void WorkingWithCollections()
{
    List<string> theCollection = new List<string>() { "Inception", "Shrek", "Ice Age", "The Matrix", "Rambo" };

    theCollection.Should().NotBeEmpty()
        .And.HaveCount(5)
        .And.OnlyHaveUniqueItems()
        .And.ContainInOrder(new[] { "Inception", "Shrek", "Ice Age", "The Matrix", "Rambo" })
        .And.ContainItemsAssignableTo<string>();
}

Because of the names that Fluent Assertions uses, I don’t need to explain what these methods are doing. If I would write the code in plain English it would say this:

The variable theCollection should not be empty, have a count of 5 items, should only have unique items, contain (in order) “Inception”, “Shrek”, “Ice Age”, “The Matrix”, “Rambo”, and it should only contain items that are of the type string.

In one line of code, I have 5 assertions that assert a list.

Collection Of Objects

This idea also works on a collection of objects. In the business layer of the demo application is a method that returns 3 movies.

new Movie
{
    Id = 1,
    Rating = 5,
    ReleaseDate = new DateTime(2001, 7, 12),
    Title = "Shrek"
},
new Movie
{
    Id = 2,
    Rating = 3,
    ReleaseDate = new DateTime(2010, 7, 22),
    Title = "Inception"
},
new Movie
{
    Id = 3,
    Rating = 4,
    ReleaseDate = new DateTime(1999, 6, 17),
    Title = "The Matrix"
}

I want to check if the number of movies returned is indeed 3, there is a movie with the title ‘The Matrix’, all items are of the type Movie, and the rating of all movies is under 5.

After translating this to C# and Fluent Assertions, this is the result:

[Fact]
public void WorkingWithCollectionObjects()
{
    MyMovies myMovies = new();

    List<Movie> result = myMovies.GetAll().ToList();

    result.Should().NotBeEmpty()
        .And.HaveCount(3)
        .And.ContainItemsAssignableTo<Movie>()
        .And.Contain(x => x.Title == "The Matrix")
        .And.OnlyContain(x => x.Rating < 5);
}

Checking The Execution Time

If you are running a piece of code that does a lot, it could take a while to finish. But as a really good developer, you are always improving your code, making it faster and better.

To make sure the execution time of your method(s) stays within a specific time, you can write unit tests to test that. I am not a fan of these kinds of tests, because the time a method runs depends on a lot of factors:

  • OS
  • Environment (the hardware)
  • Internet speed (if you are running your app online)
  • Shared environments (like shared hosting)

There are more, but these can influence the execution time of a method.

But for argument’s sake, let’s take a look at the Fluent Assertion way of testing execution time.

[Fact]
public void TestExecutionTime()
{
    Action someAction = () => DoSomething();
    someAction.ExecutionTime().Should().BeLessThanOrEqualTo(650.Milliseconds());
}

private void DoSomething()
{
    // Cleaning the room
    Thread.Sleep(100);
    // Washing the car
    Thread.Sleep(2); // The wife took the car, so I can't wash it
    // Typing a new blog
    Thread.Sleep(500);
    // I am tired. I've done enough for today...
}

The method DoSomething represents the chores I need to do today. Each chore will take me some time (in milliseconds). The test will execute the method and it will check if the ExecutionTime is less or equal to 650 milliseconds.

Why not 602 milliseconds? Because the registration, execution, and calculating of it all takes longer than the Thread.Sleeps’ together. The thread needs to start, and that will be added to the execution time.

Conclusion On Using Fluent Assertions With C# And XUnit

Although this article shows you using Fluent Assertions with C#, it does not cover all the possibilities. This library has way more to offer that will make your life a lot easier. Well, with unit testing that is. Not sure about your personal life…

Anyway… Since I started with Fluent Assertions I didn’t go back to the old xUnit assertions in my own projects. Some employers and clients demanded to us xUnit, which is fine.

If you like the way Fluent Assertions works, please check out their documentation. They have good examples with clear explanations of how to use the extensions.

Table Of Contents
Kens Learning Curve