// BLOG
Why We Removed FluentAssertions from Akka.NET
Petabridge was built to support Akka.NET, not the other way around.
Back at the very end of March we shipped Akka.NET v1.5.64. We’ve done a handful of releases since then, but I want to come back to this one specifically, because it contained a change that’s been a long time coming: we removed FluentAssertions as a dependency from every Akka.NET TestKit package.
If you write tests against Akka.NET, this might be a breaking change for you. So let me walk through exactly what changed, why we did it, and what you should do about it.
What changed
We took a dependency on FluentAssertions inside the Akka.NET TestKit a few years ago, and we were happy users the whole time. It’s a good library. Our test code leaned on it, and so did a lot of yours, because the TestKit shipped it to you transitively. If you referenced Akka.TestKit.Xunit2, you got FluentAssertions in your build whether you asked for it or not.
As of 1.5.64, it’s gone. Our TestKit packages no longer reference FluentAssertions at all. We moved our own test suites over to vanilla xUnit assertions and did the xUnit 3 migration at the same time. Going forward, the TestKit and everything we build on top of it uses plain xUnit assertions, nothing else.
graph LR
subgraph Before["Before 1.5.64"]
A1[Your test project] --> B1[Akka.TestKit.Xunit2]
B1 --> C1[xUnit]
B1 --> D1[FluentAssertions]
D1 -.transitive.-> A1
end
subgraph After["1.5.64 and later"]
A2[Your test project] --> B2[Akka.TestKit.Xunit2]
B2 --> C2[xUnit]
end
If your tests use FluentAssertions today only because the TestKit pulled it in for you, that reference is going to disappear when you upgrade, and your .Should() calls will stop compiling. That’s the breaking part. I’ll cover migration at the end.
Why we did it
In January 2025 I wrote a post called Relicense or Die about FluentAssertions’ decision to move from the permissive MIT license to a commercial license, in partnership with Xceed, a company that had no prior relationship with the project.
I want to be very clear about something: I have no problem with FluentAssertions relicensing. None. When a package gets popular, the maintainers have to make a call about whether they can keep pouring time into it for free, or whether they need to get paid to keep it alive. That’s a fair question, and it’s the entire reason I titled that post the way I did. Is it better to relicense or die? For a lot of projects, relicensing is the right answer.
I’ve said this before. When IdentityServer relicensed back in 2021, I backed it publicly, and I think it was the right call for their business. Relicensing probably was for FluentAssertions too. It turns the open source project into the product. Instead of monetizing on the side through consulting or OpenCore offerings like Phobos, the library itself becomes the thing you sell. That’s a legitimate model. No shade.
So if I’m fine with it, why pull it out of Akka.NET?
Because we were one of the largest distributors of it. As of early 2025 we were the second most popular package on NuGet consuming FluentAssertions, behind only one of FluentAssertions’ own packages. We’ve driven somewhere around three to four million installs. Against the roughly 600 million total installs FluentAssertions has, that’s maybe 1% or less, so we’re not a meaningful slice of their overall distribution. But it matters a lot to our users, because every one of those installs landed on someone running Akka.NET.
And that’s the crux of it. I’m fine with FluentAssertions being commercial. I’m fine with you pinning an old MIT-licensed version in your own projects if that’s what you want to do. What I’m not willing to do is put Akka.NET in the position of being the distributor that hands a now-commercial component to every single person who uses our framework.
Akka.NET should be permissionsless to use
This isn’t a one-off reaction. It comes straight out of why Petabridge exists in the first place.
I started this company because of how bad the .NET open source ecosystem was when I was running my last company, MarkedUp. If you want to go really far back, I wrote a post in 2014 called “The Profound Weakness of the .NET Open Source Ecosystem”” that ended up on the front page of Hacker News. Akka.NET was about seven months old at that point. I’d started writing the first lines of it around Thanksgiving of 2013, and we had our first beta versions on NuGet by June or July 2014.
The gist of that post: I was at a small venture-funded startup burning time building things that should already have existed. A CQL3 driver for Cassandra, because there was no first-party one. Socket server middleware, because there wasn’t any. File locks. Basic plumbing. If I’d built MarkedUp in Node.js, which was the hot stack at the time, I wouldn’t have had most of those problems. .NET’s open source story back then, before .NET Core and the wave of projects that came after, was genuinely pathetic.
That experience is the whole reason Petabridge has the mission it does. Our vision, the one we show every person we hire and come back to every time we do OKRs, is to enable people to build the world’s most important software by being a leading open source vendor in the .NET ecosystem, mentioned in the same breath as Microsoft. The way you’d think of JetBrains or Unity.
Here’s the part that makes the FluentAssertions decision obvious once you accept it: to do open source well, it has to be permissionless. You can’t deliver on the promise if there’s a license check and a procurement step in the way. The moment you add those, you cut off a huge portion of the people who could otherwise pick up your software and run with it on a Saturday afternoon with no purchase order and no legal review.
Petabridge was built to support Akka.NET. Not the other way around. We stood the company up to handle the requests pouring in from the outside world, and we make our money on the things around the framework: consulting, training, support, and OpenCore products like Phobos. Petabridge supports Akka.NET, Akka.NET does not support Petabridge. That might be a bad business model. Some maintainers in this ecosystem would tell me it’s stupid. But it’s what I believe, because of what I lived through at my last company.
So in that spirit, having a transitive dependency turn part of the Akka.NET experience into something that requires a commercial license doesn’t align with our mission. Thus, FluentAssertions had ot go.
We’ve had to defend this position before. When Lightbend, who built the original Akka on the JVM, changed their license in 2022 from Apache to the Business Source License, people asked whether Akka.NET was affected.
It wasn’t and, as you can see four years later, still isn’t.
What this means for you, and how to migrate
If you write tests against Akka.NET, here’s the practical impact of upgrading to 1.5.64 or later.
The TestKit itself is fine. All the Akka.NET-specific assertion helpers, ExpectMsg, AwaitAssert, ExpectNoMsg, the TestProbe APIs, and so on, are unchanged. We didn’t touch those. They never depended on FluentAssertions.
What breaks is your own use of FluentAssertions that came in transitively. If your test projects call .Should().Be(...) and you never added a direct FluentAssertions package reference yourself, those calls will stop resolving once you upgrade, because the TestKit is no longer dragging the package in for you.
You have two options:
-
Keep using FluentAssertions. Add a direct package reference to it in your own test projects. If you’re comfortable with the commercial license terms, or you pin to the last MIT-licensed version, nothing about your test code has to change. This is a completely reasonable choice, and it’s the lowest-effort path if you have a large existing suite.
-
Move to xUnit assertions, like we did. Replace your
.Should()calls withAssert.*. The mechanical mappings cover the overwhelming majority of real test code:// FluentAssertions // xUnit result.Should().Be(expected); Assert.Equal(expected, result); result.Should().NotBe(x); Assert.NotEqual(x, result); value.Should().BeTrue(); Assert.True(value); value.Should().BeFalse(); Assert.False(value); obj.Should().BeNull(); Assert.Null(obj); obj.Should().NotBeNull(); Assert.NotNull(obj); obj.Should().BeOfType<Foo>(); Assert.IsType<Foo>(obj); count.Should().BeGreaterThan(0); Assert.True(count > 0); collection.Should().Contain(x); Assert.Contains(x, collection); collection.Should().BeEmpty(); Assert.Empty(collection); collection.Should().HaveCount(3); Assert.Equal(3, collection.Count); act.Should().Throw<TException>(); Assert.Throws<TException>(act);
Watch the argument order. xUnit puts the expected value first; FluentAssertions reads left to right off the actual value. So
result.Should().Be(expected)becomesAssert.Equal(expected, result), notAssert.Equal(result, expected). Get it backwards and your tests still pass, but the failure messages come out inverted and waste your time the first time something actually breaks. It’s the most common mistake in the conversion. Past that, most of this is a mechanical find-and-replace.
If you have a large test suite and want to automate the conversion, check out fluentassertions-migrator — a .NET tool that mechanically rewrites FluentAssertions calls to their xUnit equivalents.
Both paths work. We picked plain xUnit for our own suites because it’s the smallest possible dependency surface, and because it keeps the TestKit permissionless for everyone downstream, which is the entire point.
That’s the change, and that’s the why. No shade on FluentAssertions, use it wherever you like in your own code. But Akka.NET stays free to use and permissionless to try. That commitment isn’t going anywhere.
The full release notes are here.
Observe and Monitor Your Akka.NET Applications with Phobos
Phobos automatically instruments your Akka.NET applications with OpenTelemetry — traces, metrics, and logs with built-in dashboards.
Enjoyed this post? Subscribe to our newsletter for more insights on distributed systems, Akka.NET, and .NET + AI.
// COMMENTS