Framework
fluentassertions/fluentassertions avatar
fluentassertions/fluentassertions

Fluent Assertions: readable .NET assertions, and the maintenance work behind them

A very extensive set of extension methods that allow you to more naturally specify the expected outcome of a TDD or BDD-style unit tests. Targets .NET Framework 4.7, as well as .NET Core 2.1, .NET Core 3.0, .NET 6, .NET Standard 2.0 and 2.1. Supports the unit test frameworks MSTest2, NUnit3, XUnit2, MSpec, and NSpec3.

3,813 stars720 forksC#NOASSERTION

At a glance

What is it?
The library that made Should().Be() a habit across .NET teams, now on version 8 with approval tests guarding its own public API and a commercial partner attached.
Who is it for?
Fluent Assertions is at its most convincing in the small, and version 8 is what happens when a widely used assertion library has to keep its API stable while adding features. The core is two lines of code and a sentence of failure output, which is exactly why it stuck.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly C#, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • fluentassertions/fluentassertions3,813 ★ · -1
Star history
Repository2026-10-072026-10-25
fluentassertions/fluentassertions3.8k3.8k

-1 Stars in the last 18 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Two lines that explain the whole library

The README's example is the best argument Fluent Assertions has, and it fits in five lines:

csharp
IEnumerable<int> numbers = new[] { 1, 2, 3 };

numbers.Should().OnlyContain(n => n > 0);
numbers.Should().HaveCount(4, "because we thought we put four items in the collection");

The second line is meant to fail, and the failure message is the payoff:

text
Expected numbers to contain 4 item(s) because we thought we put four items in the collection, but found 3.

Notice two things a bare Assert.Equal would not give you. The reason string is not a separate parameter or a comment, it is part of the assertion, so the output carries your intent. And the failure names the subject variable, numbers, rather than printing a raw collection dump.

The library works by extension methods on Should(), which is why it can cover so many types without you adopting a base class or a wrapper type. It targets .NET Framework 4.7, .NET Core 2.1 and 3.0, .NET 6, and .NET Standard 2.0 and 2.1, so it drops into almost any .NET project that still exists.

The Approval.Tests catch API breaks before your users do

This is the part of the repository that most surprises people who arrive from NuGet, and it is the strongest signal about how the project is run. There is a set of tests that uses the Verify project to check whether a change has altered the public API surface of the library. If you modify a public signature, those tests fail.

The workflow around it is documented rather than left to guesswork. You verify the changes, decide they are intended, and then accept them with `AcceptApiChanges.ps1` or the `AcceptApiChanges.sh` equivalent, both of which sit at the repository root. There is also a JetBrains Verify Support plug-in so you can compare and accept changes from inside Rider.

For a library with this many dependents, that is the correct arrangement. An assertion library is unusually exposed, because removing or renaming a method breaks compilation in every test suite that used it. Making the public surface an explicitly reviewed artifact rather than an incidental consequence of a pull request is how version 8 has been able to keep adding assertions without breaking people.

The repo root also shows the project using newer .NET tooling than you might expect: FluentAssertions.slnx, the slnx solution format, a NodeVersion file, a package.json for documentation spell-checking through cSpell, and qodana configuration for static analysis.

Version 8 added assertions, then spent 8.10 and 8.11 making them faster and clearer

Release 8.9.0, published on 2026-03-16, is the feature release. Its headline is support for Span<T>, ReadOnlySpan<T>, Memory<T>, and ReadOnlyMemory<T>, which is a real gap-closing change because those types cannot be handled by the older collection assertions. It also allows excluding all properties by type from BeEquivalentTo.

That release also carries a warning worth reading: projects targeting .NET 9 need .NET SDK 9.0.200 or later, because earlier versions produce compile errors from invalid overload resolution. A library that adds overloads can break consumers through overload resolution alone, and this is that situation documented in advance.

Release 8.10.0 on 2026-05-12 is a performance and diagnostics release. BeEquivalentTo for large unordered collections was significantly sped up, which matters because that assertion is the most expensive one in the library. It also added ComparingNullCollectionsAsEmpty and ComparingNullStringsAsEmpty options, and made extraneous item messages include the original index so you can find the offending element faster. Path-based rules on value-semantic types now fail with a descriptive error instead of something opaque.

Release 8.11.0 on 2026-09-14 adds ThatSatisfy for methods and properties, a set of set-theory collection assertions named BeSupersetOf, BeProperSubsetOf, and BeProperSupersetOf, round-trip BeJsonSerializable assertions, and support for asserting on delegates that return a value task. StringAssertions gained HaveLineCount and ContainLine, which is useful for multi-line text such as error messages. BeEmpty on IEnumerable<T> now lists the first ten items, and the stack trace cleanup continued. The pattern across all three is a library that keeps extending its assertion vocabulary while treating failure output as a feature.

Building it takes Visual Studio 2026 or a recent Rider

The build requirements are a signal about where the project sits. You need Visual Studio 2026 version 18.0 or later, or JetBrains Rider 2025.3. You also need both the .NET Framework 4.7 SDK and the .NET 10.0 SDK installed, which is an unusual pairing but makes sense for a library that still supports .NET Framework 4.7 while building against the current runtime. The README points at global.json for the current minimum required version.

Installing the package for your own project is the ordinary one-liner:

bash
dotnet add package FluentAssertions

Building the library itself is script driven. `build.ps1` builds, runs the unit tests, and packages the code, and the repository also carries build.cmd and build.sh for other shells. If you have the Fallout tool installed you can just run `fallout`, and the README suggests `--help` for the available options or `--plan` to see what the scripts do.

The credits list explains the tooling: Fallout for CI automation by Matthias Koch, xUnit by Brad Wilson, Coverlet by Toni Solarin-Sodara, GitVersion for deriving semantic versions from the git log, ReportGenerator for coverage output, StyleCopyAnalyzer, and Roslynator. Versions follow Semantic Versioning, and the README notes that releases since a point in the series are hosted as tags on the repository.

Xceed is now an official partner, which changes who to ask

The README has a section on the Xceed partnership that deserves more attention than it usually gets. The library was originally authored by Dennis Doomen with Jonas Nyrup as what the README calls the productive side-kick. Xceed is now an official Partner to Fluent Assertions, with a linked FAQ explaining what that means for users, and the release notes for 8.10.0 include a change rerouting the docs link to Xceed.

That last detail is the one to notice. The documentation that used to be the reference for this library now points somewhere else, which tells you the project's centre of gravity has partly moved to a commercial entity. Both authors are still visible in the release notes, with Dennis Doomen taking most of the feature and performance work and Jonas Nyrup handling the internals cleanup such as the LSP-style stack trace work and package management.

If you are picking a library for a long-lived codebase, the practical question is not whether version 8 is good but who carries it forward. The repository is not archived and the last push was on 2026-09-21, days before the 8.11.0 release on 2026-09-14, so the work is current.

The topics list hacktoberfest alongside assertions, bdd-style, c-sharp, tdd, mstest, and xunit, which matches the library's claim to serve the TDD and BDD styles rather than one test framework. The project description lists MSTest2, NUnit3, XUnit2, MSpec, and NSpec3 as supported frameworks.

What the README leaves to fluentassertions.com

The README is an overview and an index. It gives you the About paragraph, the example, the build commands, and then a set of links to fluentassertions.com covering background information, usage documentation, an extensibility guide, support information, and a tips and tricks page.

That division is sensible for a project this size, because the assertion vocabulary is far too large to enumerate in a README. Anyone evaluating this library needs the usage documentation; anyone extending it needs the extensibility guide, which is the page that explains how to write assertions that look native rather than bolted on.

The repository root reinforces the same division. CONTRIBUTING.md is linked and is the place with the actual contribution rules, while the README keeps to welcoming you and pointing there. The tree carries agents.md, CLAUDE.md, profile.yaml, and a .junie directory, which is a fairly extensive set of files aimed at coding assistants working inside the repository.

So the practical picture is this. Installing is one command, the first assertion takes about a minute to write, and the failure messages are the feature that justifies the library. Everything past that, from custom assertion authors to the exact behaviour of BeEquivalentTo, is documented off the repository page.

Editorial conclusion

Fluent Assertions is at its most convincing in the small, and version 8 is what happens when a widely used assertion library has to keep its API stable while adding features. The core is two lines of code and a sentence of failure output, which is exactly why it stuck. The interesting engineering sits elsewhere: the Approval.Tests that diff your public API on every build, the stack trace cleanup that keeps failures pointing at your test rather than at Should(), and the release notes that show BeEquivalentTo getting faster rather than just bigger. Installation is one `dotnet add package FluentAssertions` and the target frameworks cover anything from .NET Framework 4.7 upward. Read the extensibility guide on fluentassertions.com if you want custom assertions, and use AcceptApiChanges.ps1 when you deliberately change one, because the approval tests will fail otherwise.

Frequently asked questions

What are fluent Assertions?

Fluent Assertions is a library of extension methods that let you express the expected outcome of a .NET test in a readable chain, typically starting with Should(). The README frames it as supporting both TDD and BDD styles across .NET Framework, .NET Core, and .NET Standard targets, and it works with MSTest, NUnit, xUnit, MSpec, and NSpec.

How do I use Assertions in C#?

Add the package with dotnet add package FluentAssertions, then call Should() on the value under test and chain an assertion onto it. The README example asserts on a collection with OnlyContain and HaveCount, and shows that a because argument you pass is repeated in the failure message.

How does Fluent Assertions keep its public API from breaking?

It uses approval tests built on the Verify project that diff the public API surface on every build, so a changed signature fails the build. You then review the diff and accept it with AcceptApiChanges.ps1 or AcceptApiChanges.sh, or from inside Rider with the Verify Support plug-in.

Does Fluent Assertions work with .NET 9 and .NET 10?

Version 8.9.0 adds support for Span<T>, ReadOnlySpan<T>, Memory<T>, and ReadOnlyMemory<T>, and its notes warn that projects targeting .NET 9 need .NET SDK 9.0.200 or newer because earlier SDKs hit invalid overload resolution. Building the library itself requires both the .NET Framework 4.7 SDK and the .NET 10.0 SDK.

Official sources

  1. fluentassertions/fluentassertions on GitHub
  2. Issues
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/fluentassertions-fluentassertions.svg)](https://hysenlabs.com/projects/fluentassertions-fluentassertions)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/fluentassertions-fluentassertions.svg)](https://hysenlabs.com/projects/fluentassertions-fluentassertions)

Was this useful?