xUnit v3 best improvements
After a long wait, xUnit version 3 is finally here. This isn't just a small update; it’s a major overhaul that brings some massive quality-of-life improvements and fixes "pain points" that have bothered .NET developers for years.
In this post, we’re covering the three most interesting features in the new release and how you can start using them today.
1. The Death of the "Collection Fixture" Pain: Assembly Fixtures
One of the biggest frustrations in xUnit v2 was sharing state (like a database or a heavy object) across multiple test classes. To do this, you had to use ICollectionFixture, which forced all tests in that collection to run sequentially.
If you had a heavy database fixture shared by three classes, xUnit would run them one by one, killing your test suite's performance.
**The Solution: [assembly: AssemblyFixture]**
With xUnit v3, you can now define a fixture at the assembly level. By simply adding:
[assembly: AssemblyFixture(typeof(MyGlobalFixture))]
xUnit creates one instance of the fixture for your entire assembly and injects it into your constructors. The best part? Your tests can now run in parallel while still sharing that single instance. No more choosing between shared state and speed!
2. Built-in CancellationToken Support
If you’ve ever written an async test and seen a warning about missing a CancellationToken, you’ll love this. Previously, you had to manually manage CancellationTokenSource within your tests.
xUnit v3 introduces TestContext.Current , which gives you access to a wealth of metadata about the running test, including a built-in CancellationToken.
public async Task MyTest()
{
var token = TestContext.Current.CancellationToken;
await _service.DoWorkAsync(token);
}
This token is automatically cancelled if the test hits a timeout or if the test runner is stopped. It’s a much cleaner, "ASP.NET Core-like" way of handling async operations in your tests.
3. Dynamic Test Skipping: SkipWhen and SkipUnless
In the past, skipping a test was a static affair—you either put Skip = "Reason" on the attribute or you didn't.
xUnit v3 introduces more flexible, logic-based skipping:
SkipWhen: Skips the test if a certain condition is true.SkipUnless: Skips the test unless a condition is met.
This is perfect for tests that should only run on specific Operating Systems, CI environments, or if certain configuration keys are present. You just point the attribute to a static property in your class:
[Fact]
[SkipWhen(nameof(IsDatabaseOffline))]
public void TestDatabase() { ... }
public static bool IsDatabaseOffline => // your logic here
Getting Started with v3
Because of the significant refactoring involved, v3 comes as a brand-new NuGet package (xunit.v3.core). You’ll also need to update your runner to xunit.v3.runner.visualstudio.
One other fun change: test projects are now Executables. You can simply run the project like a console application to execute your tests!
Final Thoughts
Whether it's the performance boost from Assembly Fixtures or the developer experience of TestContext, xUnit v3 is a must-upgrade.
Watch the full walkthrough here: https://www.youtube.com/watch?v=3C_BCHWAnBU