Fluent Assertions in C#

In this post I am going to talk about the fluent assertions library. Maybe many of you already know and use it; here, we will go into detail about how to use it and why.

 

1 - What is Fluent Assertions?

Fluent Assertions is a completely open source C# library that we are going to use to improve our assertions inside our unit tests. The way fluent assertions works is with extension methods, which makes assertions read like a normal sentence, which means they are easier to understand.

 

As a particular note, if you come from another programming language, you will probably find fluent assertions easy to understand, since this is the default style in other languages too.

 

NOTE: Starting from version 8 (released in 2025), Fluent Assertions is changing its license and will become paid for commercial use, link.

 

2 - Example of Fluent assertions in C#

First of all, we need to include the NuGet package FluentAssertions, and that's it, after that, we can leverage its full potential.

When choosing a test framework, I usually prefer xUnit, but fluent assertions works with all of them, so you can pick your favorite.

Now, let’s compare how our usual asserts work vs. fluent assertions.

  • Note: All this code is available on GitHub.

 

2.1 - Basic checking with fluent assertions

For this example, we are going to check basic items. Generally, the simplest thing we do in our tests is check properties, especially strings and booleans.

[Fact]
public void TestCode1()
{
    string value1 = "Este es el valor de mi cadena de texto";
    bool booleanValue = true;

    Assert.Equal("Este es el valor de mi cadena de texto", value1);
    Assert.True(booleanValue);
}

And this would be the same check using Fluent Assertions.

[Fact]
public void TestCode1b()
{
    string value1 = "Este es el valor de mi cadena de texto";
    bool booleanValue = true;

    value1.Should().NotBeNull();
    value1.Should().Be("Este es el valor de mi cadena de texto");
    booleanValue.Should().BeTrue();
}

As you can see, we are always using the extension method Should(). There are two reasons for this: the first is that it reads like regular English, and the second is that it allows chaining all the fluent assertions extension methods without problem.

After that, we saw Be() and NotBe() which are equivalent to == and !=. But this is just the tip of the iceberg; let’s analyze, in detail, the different options we have.

 

2.2 - Fluent Assertions with string

When comparing strings, we can do a lot of things. The basic one is Be(), as we have seen. But what if we want to compare ignoring case? In that case, we can use BeEquivalentTo():

[Fact]
public void TestCode2()
{
    string value1 = "Este es el valor de mi cadena de texto";
    
    value1.Should().BeEquivalentTo("este es el valor de mi cadena de texto");
}

For example, BeLoweredCase() checks that the string is fully lowercase, while BeUpperCased() checks that it’s all uppercase.

 

Other important checks are BeEmpty() or BeNull(), which obviously check if a string is empty or null. And of course, we have the negative forms of all we’ve seen so far.

NotBe(), NotBeEquivalentTo(), NotBeNull(), NotBeNullOrEmpty(), and NotBeUpperCased().

 

Finally, before wrapping up with strings, we can match a regular expression with the method MatchRegex().

 

2.3 - Fluent assertions with other types

I’m not going to explain each type individually, because they are all quite similar. With Be..() we check if it's equal, with NotBe…() we check if it's different. And as I said, this applies to all types, booleans, integers, datetime, etc.

 

For example, with a DateTime (or DateOnly) object we can check if a date is before or after a certain moment:

[Fact]
public void TestDates()
{
    DateOnly dt = new DateOnly(2000,01,01);
    dt.Should().BeBefore(DateOnly.FromDateTime(DateTime.UtcNow));
}

In the end, I can’t stop at every single element, because that's what the documentation is for, but I’ll focus on important points.

 

2.4 - Fluent Assertions with Collections

Where I will stop is in the checking of collections. For me, this is where the real power of the library lies.

One thing I do a lot is, when a method returns a list, I check the size of the list and then take the first item into a variable to check its key properties.

  • Note: I don’t check all the properties, because if there’s a mapper or something similar, I already have that tested elsewhere.

If we do this action with the usual assertions, the code is pretty long and ugly:

private record ExampleObj(string Name, int Age);
[Fact]
public void TestCode3()
{
    List<ExampleObj> list = new List<ExampleObj>()
    {
        new ExampleObj("Name1", 24),
        new ExampleObj("Name2", 45),
        new ExampleObj("Name3", 30),
    };
    
    Assert.True(list.Any());
    ExampleObj firstElement = list.First();
    Assert.Equal("Name1", firstElement.Name);
    Assert.Equal(24, firstElement.Age);
}

As we see, I’m first checking if the list has any elements, and then taking the first one to verify those properties.

 

This is where FluentAssertions shines. In the complexity of the test, the library brings simplicity, because the same action can be written in a much more natural way.

[Fact]
public void TestCode3b()
{
    List<ExampleObj> list = new List<ExampleObj>()
    {
        new ExampleObj("Name1", 24),
        new ExampleObj("Name2", 45),
        new ExampleObj("Name3", 30),
    };

    list.Should().NotBeNull()
        .And.Contain(a => a.Name == "Name1")
        .Which.Age.Should().Be(24);
}

It may seem more or less complicated, or even surprising, especially due to the syntax, but the truth is that if you know English, you’ll understand without any complication what this code is doing.

 

Within collections, we can check if they are sorted by certain elements or not, which is also really useful. I use this a lot, especially when testing pagination with filtering and sorting, BeInAscendingOrder and BeInDescendingOrder. Of course, we can specify which property we want to check for ordering.

 

2.5 - Exception checking with fluent assertions

Just as in the previous point I find FluentAssertions superior, and a reason that could fully justify its use just for how it treats collections, with exceptions it’s the opposite.

The way to check exceptions in xUnit is quite straightforward:

Assert.Throws<NotImplementedException>(() => ThisMethodReturnsException());

Now, in fluent assertions it’s a bit more complex, you have to use an action delegate and from there check the exception as follows:

[Fact]
public void TestCodeExceptionFA()
{
    Action action = () => ThisMethodReturnsException();
    action.Should().Throw<NotImplementedException>();
}

Of course, it’s not the end of the world; it’s a little longer, yes, but the use case for checking exceptions is much lower than with any other type, but there it is.



Conclusion

In this post, we've seen an introduction to FluentAssertions and why you will often come across it in companies. It has features, such as everything related to collections, that, in my opinion, are superior to regular checks of the “default” frameworks.


Also, if you come from other languages it may be easier for you, as the syntax is similar, and of course easy to read, since it’s plain English with many functions that check things out of the box. Like with any library that’s more or less large, it does require some learning curve, so having some knowledge about it is definitely a plus.

This post was translated from Spanish. You can see the original one here.
If there is any problem you can add a comment bellow or contact me in the website's contact form

Uso del bloqueador de anuncios adblock

Hola!

Primero de todo bienvenido a la web de NetMentor donde podrás aprender programación en C# y .NET desde un nivel de principiante hasta más avanzado.


Yo entiendo que utilices un bloqueador de anuncios como AdBlock, Ublock o el propio navegador Brave. Pero te tengo que pedir por favor que desactives el bloqueador para esta web.


Intento personalmente no poner mucha publicidad, la justa para pagar el servidor y por supuesto que no sea intrusiva; Si pese a ello piensas que es intrusiva siempre me puedes escribir por privado o por Twitter a @NetMentorTW.


Si ya lo has desactivado, por favor recarga la página.


Un saludo y muchas gracias por tu colaboración

© copyright 2025 NetMentor | Todos los derechos reservados | RSS Feed

Buy me a coffee Invitame a un café