xUnit.net - Open Source Sponsorship
Every test you write needs a place to run. Before the assertions fire, before the snapshots compare, before a single line of application code gets exercised, something has to discover your tests, spin up the runtime, manage parallelism, and report results. For most .NET developers, that something is xUnit.net, and it’s so good at its job that you probably never think about it.
That invisible reliability is exactly why we’re sponsoring it. Let’s hear from the current maintainer as to what motivated them to build such a foundational project:
"I have been a developer using Test Driven Development for more than 20 years, and xUnit.net represents the codified guidance that Jim Newkirk and I would frequently give at development conferences to teach others how to get the most out of unit testing. Now that I’m retired, sponsorships like this help keep xUnit.net moving forward."
– Brad Wilson
Join us in congratulating our sixth open-source sponsorship, xUnit.net.
What is xUnit.net?
xUnit.net is a free, open-source, community-focused unit testing tool for .NET. Created by Brad Wilson and James Newkirk (the original author of NUnit), xUnit.net was built from the ground up to incorporate the lessons learned from earlier testing frameworks. The result is a lean, extensible, opinionated framework that trusts developers to write tests the way they want.
Developers commonly refer to the project as “xUnit,” though the official name is xUnit.net. Either is perfectly acceptable in conversation.
xUnit.net v3 is the current major release, supporting .NET 8.0 or later and .NET Framework 4.7.2 or later. It runs everywhere you’d expect: the .NET SDK command line, Visual Studio, Visual Studio Code, JetBrains Rider, and NCrunch. The project is part of the .NET Foundation, licensed under Apache 2.0, and has earned over 4,500 stars on GitHub with 27,400+ projects depending on it. Brad Wilson alone has made over 2,600 contributions to the repository.
The framework’s philosophy rests on a few core principles: simplicity (tests are just classes with methods), extensibility (almost everything can be overridden or replaced), and sensible defaults (parallelism is on by default, test class isolation is the norm). These design decisions make xUnit.net feel less like a framework you fight and more like one that stays out of your way.
What sets xUnit.net apart from earlier frameworks is how it handles the test lifecycle. There’s no [SetUp] or [TearDown]. Instead, constructors and IDisposable handle per-test setup and cleanup. Shared state across tests uses IClassFixture<T> or ICollectionFixture<T>, making resource lifetimes explicit rather than hidden behind attribute conventions. This approach makes tests easier to reason about and reduces the surprises that come from shared mutable state between test methods.
Getting Started
To add xUnit.net to an existing test project, install the NuGet package:
Shell
dotnet add package xunit.v3 That’s it. No runner packages to install separately, no additional configuration files. xUnit.net v3 includes a built-in runner, so you execute your tests with dotnet run rather than dotnet test (though dotnet test still works via the Visual Studio adapter).
Here’s a simple test class showing both [Fact] (a single test case) and [Theory] (a parameterized test):
Csharp
namespace MyProject.Tests;
public class CalculatorTests
{
[Fact]
public void Adding_two_and_two_returns_four()
{
var result = 2 + 2;
Assert.Equal(4, result);
}
[Theory]
[InlineData(1, 1, 2)]
[InlineData(5, 3, 8)]
[InlineData(0, 0, 0)]
public void Addition_is_correct(int a, int b, int expected)
{
var result = a + b;
Assert.Equal(expected, result);
}
}Run the tests:
Shell
dotnet runxUnit.net v3 In-Process Runner v3.2.2 (64-bit .NET 10.0.8)
Discovering: MyProject.Tests
Discovered: MyProject.Tests
Starting: MyProject.Tests
Finished: MyProject.Tests
=== TEST EXECUTION SUMMARY ===
MyProject.Tests Total: 4, Errors: 0, Failed: 0, Skipped: 0, Not Run: 0, Time: 0.032sxUnit.net at Duende
xUnit.net runs under every test we write at Duende Software. Every Shouldly assertion and every Verify snapshot across our products executes on xUnit.net. It powers our IdentityServer tests, our BFF Security Framework integration tests, our open-source library tests, and our end-to-end Playwright tests. When we say we care about testing, xUnit.net is where that commitment lives in code.
Here’s an example of how we use xUnit.net’s IClassFixture<> with WebApplicationFactory to integration test IdentityServer endpoints. This pattern works just as well for anyone testing their own IdentityServer deployment.
First, a minimal IdentityServer host:
Csharp
// Program.cs
using Duende.IdentityServer.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddIdentityServer()
.AddInMemoryApiScopes([
new ApiScope("api1", "My API")
])
.AddInMemoryClients([
new Client
{
ClientId = "client",
AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets = { new Secret("secret".Sha256()) },
AllowedScopes = { "api1" }
}
]);
var app = builder.Build();
app.UseIdentityServer();
app.Run();
public partial class Program;Then, integration tests that verify the discovery document:
Csharp
using System.Net;
using System.Text.Json;
using Microsoft.AspNetCore.Mvc.Testing;
namespace IdentityServerHost.Tests;
public class DiscoveryEndpointTests : IClassFixture<WebApplicationFactory<Program>>
{
private readonly HttpClient _client;
public DiscoveryEndpointTests(WebApplicationFactory<Program> factory)
{
_client = factory.CreateClient();
}
[Fact]
public async Task Discovery_endpoint_returns_success()
{
var response = await _client.GetAsync("/.well-known/openid-configuration");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
[Fact]
public async Task Discovery_endpoint_contains_expected_issuer()
{
var response = await _client.GetAsync("/.well-known/openid-configuration");
var content = await response.Content.ReadAsStringAsync();
var document = JsonDocument.Parse(content);
Assert.True(document.RootElement.TryGetProperty("issuer", out var issuer));
Assert.NotNull(issuer.GetString());
}
[Fact]
public async Task Discovery_endpoint_lists_supported_scopes()
{
var response = await _client.GetAsync("/.well-known/openid-configuration");
var content = await response.Content.ReadAsStringAsync();
var document = JsonDocument.Parse(content);
Assert.True(document.RootElement.TryGetProperty("scopes_supported", out var scopes));
var scopeList = scopes.EnumerateArray()
.Select(s => s.GetString())
.ToList();
Assert.Contains("api1", scopeList);
}
[Fact]
public async Task Discovery_endpoint_lists_supported_grant_types()
{
var response = await _client.GetAsync("/.well-known/openid-configuration");
var content = await response.Content.ReadAsStringAsync();
var document = JsonDocument.Parse(content);
Assert.True(
document.RootElement.TryGetProperty("grant_types_supported", out var grantTypes));
var grantTypeList = grantTypes.EnumerateArray()
.Select(g => g.GetString())
.ToList();
Assert.Contains("client_credentials", grantTypeList);
}
} This test class uses IClassFixture<WebApplicationFactory<Program>> to share a single test server across all four tests. xUnit.net manages the fixture’s lifecycle: it creates the factory once before the first test runs and disposes it after the last test finishes. The tests themselves verify that the IdentityServer discovery document is accessible, contains a valid issuer, advertises the api1 scope, and supports the client_credentials grant type.
You can adapt this pattern to test any aspect of your IdentityServer configuration: token endpoint responses, introspection behavior, CORS headers, or custom endpoints.
The test project only needs two package references:
Shell
dotnet add package xunit.v3
dotnet add package Microsoft.AspNetCore.Mvc.TestingAnd a project reference to your IdentityServer host. That’s all you need to start testing your identity infrastructure.
Sponsorship Details
Duende Software is sponsoring xUnit.net for the next 12 months at $250 per month (totaling $3,000 for the year).
If you benefit from xUnit.net, consider sponsoring xUnit.net on GitHub or contributing code, docs, or bug reports.
We’d like to thank Brad Wilson for his work on xUnit.net and its role in shaping how an entire generation of .NET developers write tests. This award is our third sponsorship in the testing space, following Shouldly and Verify, and it’s no coincidence. Testing is foundational to how we build secure identity software at Duende Software, and xUnit.net is the foundation under all of it.
Try It Out
If you haven’t tried xUnit.net v3 yet, now’s a good time. Check out the getting started guide and the official documentation to dive in.