On this page · 13 sections
- What actually shipped on 14 August 2026
- Native AOT: what you get and what you give up
- Parallel mode all, and the attribute that replaces CollectionBehavior
- The three support lines 4.0 drops
- The CI breakages that do not show up in code review
- Twenty new analyzer rules, and why most of them are AOT guard rails
- Sequencing the upgrade against 10 November 2026
- Should you move to Native AOT at all
- India-specific considerations
- What the maintainer situation means for your risk assessment
- FAQ
- How eCorpIT can help
- References
Summary. On 14 August 2026 the xUnit.net project shipped three packages at once: Core Framework v3 4.0.0, Analyzers 2.0.0 and the Visual Studio adapter 4.0.0. The core framework release is the first major version in 13 months, and it lands two features that have been requested since xunit/xunit#1986 was opened: Native AOT test projects, and a third parallel mode, all, that runs every test against every other test regardless of collection. It also drops three support lines in one go, which is the part that will decide your upgrade window. Microsoft Testing Platform v1 is gone, Mono is gone, and Analyzers 2.0.0 stops supporting Visual Studio 2019. The timing matters because .NET 8 and .NET 9 both reach end of support on 10 November 2026, so most teams are already inside a runtime migration when this lands. xUnit.net is used by more than 27,400 dependent projects and carries over 4,500 GitHub stars, and it is maintained by one retired developer funded by sponsorships of roughly $250 per month.
The real cost here is not the code. It is the CI configuration you have not looked at in two years.
What actually shipped on 14 August 2026
Three packages, released together, with separate release notes:
The Core Framework v3 4.0.0 release closes a gap of 13 months since 3.0.0 and 7 months since the last minor, 3.2.2. The release notes describe it as a major undertaking, and the breaking-change list runs to several dozen entries, most of them confined to extensibility APIs rather than everyday test authoring.
Analyzers 2.0.0 is the first major analyzer release in four years, following 1.0.0. It adds 16 new usage analyzers, 1 new assertion analyzer and 3 new extensibility analyzers, and it exists mainly to make Native AOT projects diagnosable at build time rather than at runtime.
The Visual Studio adapter 4.0.0 is the quietest of the three. Its own release notes say the plain thing out loud: "At some point in the future, this package will probably be entirely deprecated, since most (if not all) of the major third party runners which support VSTest, also support MTP." The adapter's last major release, 3.0.0, was 20 months earlier.
If you only read one of the three, read the core framework notes. If you only change one thing, change your CI arguments.
Native AOT: what you get and what you give up
Native AOT support is the headline. It is also the feature with the largest set of caveats, and the caveats are documented properly rather than buried.
The mechanism is source generators. Test discovery in xUnit.net has always leaned on runtime reflection, and reflection is severely limited under Native AOT, so 4.0 moves discovery of tests, theory data and attribute-driven configuration to build time. That has three consequences you cannot design around:
C# is the only supported language for Native AOT tests, because the source generators inspect C# source. F# and VB.NET test projects stay in reflection mode.
The minimum target framework for AOT mode is .NET 9, because the implementation makes extensive use of [OverloadResolutionPriority]. Reflection mode still supports .NET 8 and later, plus .NET Framework 4.7.2 and later.
Every library package now ships an AOT variant. xunit.v3 becomes xunit.v3.aot, xunit.v3.assert becomes xunit.v3.assert.aot, xunit.v3.core becomes xunit.v3.core.aot, and so on. There is no mtp-v1 AOT variant at all, because 4.0 does not support Microsoft Testing Platform v1.
The Testing with Native AOT documentation lists what stops working in AOT mode. EventSource reporting, default interface methods inherited by test classes, generic test methods, interface-based attributes such as IFactAttribute, serialization, .uniqueid overrides, and implicit or explicit operator conversions of theory data are all unavailable. Stack traces from published projects are significantly reduced.
Assertion output also changes. A failing Assert.Equal on a complex type prints the property values in reflection mode:
Assert.Equal() Failure: Values differ
Expected: Foo { IntValue = 42 }
Actual: Foo { IntValue = 2112 }
The same failure in AOT mode prints this instead:
Assert.Equal() Failure: Values differ
Expected: Foo { ··· }
Actual: Foo { ··· }
Printing property values needs reflection, so the formatter tells you it cannot see inside the object. For a team that triages CI failures from log output rather than by reattaching a debugger, that is a material regression, and it is the single best argument for keeping integration suites in reflection mode while moving only the fast unit suites to AOT.
| Capability | Reflection mode | Native AOT mode |
|---|---|---|
| Languages supported | C#, F#, VB.NET | C# only |
| Minimum target framework | .NET 8, or .NET Framework 4.7.2 | .NET 9 |
| Generic test methods | Supported | Not available |
| Complex-object assertion output | Prints property values | Prints Foo { ··· } |
| Test discovery mechanism | Runtime reflection | Build-time source generators |
| Serialization of test cases | Supported | Not available |
| Third-party runners on published binaries | Supported | Generally not recognised |
The last row deserves a note. Once you run dotnet publish on an AOT test project, the output is a stand-alone executable with no outwardly visible relationship to .NET, and third-party runners will not recognise it. xUnit.net's own multi-assembly runners still work if you pass the executable as the test assembly filename, and setting <UseMicrosoftTestingPlatformRunner>true</UseMicrosoftTestingPlatformRunner> extends the Microsoft Testing Platform experience to published projects. In unpublished form, everything behaves normally, including Visual Studio, VS Code and VSTest.
Parallel mode all, and the attribute that replaces CollectionBehavior
The second headline feature is full test parallelisation. Before 4.0, xUnit.net v3 offered two parallel modes, none and collections, with collections as the default. Version 4.0 adds all, which runs every test in parallel against every other test regardless of test collection or shared context.
The parallelism documentation gives the arithmetic. A single test class containing two tests, one sleeping 3 seconds and one sleeping 5 seconds, takes about 8 seconds in mode collections because both tests live in the same collection. In mode all the same class finishes in about 5 seconds. On a suite where most classes hold several independent tests, that difference compounds.
The opt-out model is layered, and the layering rule is strict: once you opt out of parallelism at one layer, you cannot opt back in at a lower layer. You can disable parallelism at the test collection, test class, test method, theory data source and theory data row levels, using [CollectionDefinition(DisableParallelization = true)], [TestClass(DisableParallelism = true)], [Fact(DisableParallelism = true)], [InlineData(42, DisableParallelization = true)] and the DisableParallelization property on TheoryDataRow respectively.
| Parallel mode | Available in | Behaviour |
|---|---|---|
none |
v2, v3 all versions | Every test runs sequentially |
collections |
v2, v3 all versions (default) | Tests in different collections run in parallel |
all |
v3 4.0 and later only | Every test runs in parallel against every other test |
all requested on older assemblies |
v2 and v3 before 4.0 | Silently treated as collections |
Algorithm conservative |
v2 2.8 and later (default) | Starts only as many tests as max parallel threads |
Algorithm aggressive |
v2 2.8 and later | Starts as many tests as possible, throttled by a SynchronizationContext |
The configuration surface moved with the feature. [assembly: CollectionBehavior] has had DisableTestParallelization, MaxParallelThreads and ParallelAlgorithm marked obsolete and un-callable, because those settings are no longer limited to collection-level parallelism. The replacements live on a new attribute:
[assembly: Parallelization(Mode = ParallelMode.All)]
[assembly: Parallelization(MaxThreads = 8)]
[assembly: Parallelization(Algorithm = ParallelAlgorithm.Conservative)]
The default parallel mode stays ParallelMode.Collections and the default algorithm stays ParallelAlgorithm.Conservative, so an upgrade does not silently change your concurrency. Mode all is opt-in, which is the correct default given how many test suites carry undeclared shared state.
One quiet warning in the mode table above is worth restating. If you set -parallelMode all in a runner and point it at a v2 assembly or a v3 assembly older than 4.0, that assembly treats the request as collections. Mixed-version solutions will run at two different concurrency levels and give you no error to explain the difference.
The three support lines 4.0 drops
This is the section that determines whether you can upgrade this quarter or next.
Microsoft Testing Platform v1 support is discontinued. The default MTP support level is now v2, currently at version 2.3.3, and the project continues to publish mtp-off packages for teams who want to keep using VSTest on the .NET 10 SDK. There is no AOT package for MTP v1, because MTP v1 is simply not an option in 4.0.
Mono support is discontinued. The release notes are direct about why: the maintainers saw occasional Mono-related issues in their own CI, Mono is now abandoned, and they do not expect to be able to get support for resolving them. Requests for Mono-related support will be declined, though severe bug fixes may still be accepted.
Analyzers 2.0.0 removes support for Visual Studio 2019. The new minimum Roslyn version is 4.11, which requires Visual Studio 2022 17.11 or later, or .NET SDK 8.0.400 or later. Both of those shipped in August 2024, so this is a two-year-old floor rather than a fresh one, but a build agent pinned to an older SDK will fail.
| Dropped support | Where it appears | Minimum replacement |
|---|---|---|
| Microsoft Testing Platform v1 | Core Framework v3 4.0.0 | MTP v2, version 2.3.3 |
| Mono | Core Framework v3 4.0.0 | .NET 8 or later, .NET Framework 4.7.2 or later |
| Visual Studio 2019 | Analyzers 2.0.0 | Visual Studio 2022 17.11, or .NET SDK 8.0.400 |
| .NET Core 3.1 through .NET 7 | Microsoft Testing Platform v2 | .NET 8 |
VSTest-based dotnet test on .NET 10 SDK |
Microsoft Testing Platform v2 | global.json opt-in to MTP |
The last row is a Microsoft change rather than an xUnit.net one, and it catches teams who assume the framework upgrade is self-contained. Microsoft's MTP v1 to v2 migration guide states that running MTP test projects with the .NET 10 SDK now requires opting in to the MTP-based dotnet test, and that the TestingPlatformDotnetTestSupport MSBuild property from MTP v1 no longer does the job. The opt-in is a global.json at the repository or solution root:
{
"test": {
"runner": "Microsoft.Testing.Platform"
}
}
Without it, the build fails with a message telling you that testing with the VSTest target is no longer supported by MTP on .NET 10 SDK and later. MTP v2 also renamed two diagnostic switches: --diagnostic-output-fileprefix became --diagnostic-file-prefix, and --diagnostic-filelogger-synchronouswrite became --diagnostic-synchronous-write.
The CI breakages that do not show up in code review
A framework upgrade that compiles cleanly can still break every pipeline you own, because most of 4.0's user-visible churn is in command-line arguments and output filenames.
Microsoft Testing Platform report switches were renamed to avoid future collisions with switches added by MTP itself. -report-ctrf is now -report-xunit-ctrf, -report-junit is now -report-xunit-junit, -report-nunit is now -report-xunit-nunit, and -report-xunit is now -report-xunit-xml. Each has a matching -filename variant that moved the same way. The two switches that already carried xunit in the name, -report-xunit-html and -report-xunit-trx, are unchanged.
Default report file extensions changed so that the auto-generated names are actually openable. The JUnit report went from .junit to .junit.xml, the NUnit report from .nunit to .nunit.xml, and the xUnit.net v2 report from .xunit to .xunit.xml. Any pipeline step that globs for *.junit will silently collect nothing, and a test-results publisher that finds no files usually reports success.
The console runner now warns on 20 deprecated switch forms rather than failing on them, including -parallel all, -quiet, -verbose, -teamcity, -trx <file>, -xml <file> and the -noclass and -nomethod filter family. Warnings are easy to miss in a 4,000-line CI log, and the release notes state plainly that anything marked as a breaking change here will be removed in the next major version. Treat the warnings as the deadline.
The console runner also became a .NET tool. You install it with dotnet tool install -g xunit-console-tool and run it with dotnet xunit-console, and it requires the .NET 10 SDK or later. It ships builds for Linux on 32-bit Arm, 64-bit Arm and 64-bit Intel or AMD, macOS on 64-bit Arm and 64-bit Intel or AMD, and Windows on 64-bit Arm, 64-bit Intel or AMD, and 32-bit Intel or AMD.
Two more changes worth putting on a checklist. The CTRF result report was realigned with the Microsoft Testing Platform CTRF report: /results/tests/[]/suite is now a string array rather than a single string, /results/tests/[]/extra/output moved to /results/tests/[]/stdout, and /results/tests/[]/extra/traits moved to /results/tests/[]/labels. And --filter now accepts the older VSTest filter syntax, which is the single most useful addition for teams porting off VSTest.
Twenty new analyzer rules, and why most of them are AOT guard rails
Analyzers 2.0.0 adds 20 rules in total. The AOT-specific ones exist because the failure they prevent would otherwise be a confusing build-time source-generator error or a missing test.
xUnit1057 requires types to be public or internal so the Native AOT source generators can reference them. xUnit1058 rejects generic collection definitions. xUnit1062 rejects open generic [Theory] methods, xUnit1063 rejects open generic test classes, and xUnit1061 rejects generic [Fact] methods in both modes because there is no data with which to close the generic. xUnit1064 and xUnit1066 reject params modifiers on theory parameters and on [MemberData] source methods. xUnit1065 rejects overloaded [MemberData] members, and xUnit1068 rejects [MemberData] pointing at an open generic type.
Two non-AOT additions are worth adopting regardless of your upgrade path. xUnit1069 flags a test with Timeout set that never references TestContext.Current.CancellationToken, which is the actual mechanism that makes a timed-out test stop running rather than merely be reported as failed. xUnit2033 flags code that re-derives a value an assertion already returned, such as calling Assert.Single and then indexing the collection again.
The extensibility analyzers xUnit3006 and xUnit3007 are the ones that will surface real bugs in older codebases. They flag test case implementations that are definitely, or might not be, serializable in reflection mode. A non-serializable test case is the classic cause of a test that runs from the command line but cannot be run individually from Test Explorer.
Roslyn 4.11 also brought a general improvement: every analyzer that inspects array usage now understands collection expressions, so [1, 2, 3] is analysed the same as new[] { 1, 2, 3 }.
Sequencing the upgrade against 10 November 2026
Two clocks are running at once, and only one of them is xUnit.net's.
Rahul Bhandari, Senior Program Manager on the .NET team at Microsoft, confirmed in June 2026 that .NET 8 and .NET 9 both reach end of support on 10 November 2026. .NET 8 shipped on 14 November 2023 as an LTS release supported for 36 months. .NET 9 shipped on 12 November 2024 as a standard-term release whose window was extended from 18 to 24 months, which lands it on the same day. .NET 10 shipped on 11 November 2025 as LTS and is supported through November 2028. Per the official .NET support policy, LTS releases get three years and STS releases get two. Applications on .NET 8 or .NET 9 keep running after 10 November 2026, but they stop receiving security updates.
That gives most teams a sensible order of operations. Move the runtime first, because .NET 10 is a hard deadline with a security consequence and xUnit.net 4.0 is not. Move to MTP v2 second, because the global.json opt-in is required on the .NET 10 SDK anyway and doing it separately isolates the failure mode. Take xUnit.net 4.0 third, in reflection mode, changing nothing except package versions and CI switch names. Only then evaluate Native AOT and parallel mode all, one suite at a time.
Reversing that order means debugging a source-generator error, a global.json error and a renamed report switch in the same pull request.
The CI cost argument is real but should be measured rather than assumed. GitHub cut the price of GitHub-hosted runners by up to 39% with effect from 1 January 2026, so every minute parallel mode all saves is now worth up to 39% less than the same minute was worth in 2025. Runner usage in public repositories stays free. GitHub also announced a $0.002 per minute platform charge on self-hosted runner usage from 1 March 2026, then postponed it after developer pushback, saying it would re-evaluate the approach; GitHub Enterprise Server was never in scope. Measure your own runner minutes against the current rate card before you build a business case. The engineering value of a faster suite is in the feedback loop, not the invoice.
Should you move to Native AOT at all
For most teams, not yet, and not everywhere.
Native AOT test projects make sense where startup time dominates, where you are already shipping AOT-compiled production binaries and want the tests compiled the same way, or where you want to catch AOT trimming problems in the test suite rather than in production. The trade is real: worse assertion output, no generic test methods, no serialization, reduced stack traces, and a source-generator dependency for any custom [Fact]-style attribute or data source you have written.
The project has shipped three sample projects to make custom extensions viable under AOT: AotRetryFact for custom discovery and execution, AotCsvDataSource for a custom data source attribute, and AotTraitExtensibility for a custom source of traits. All three consume the source-based xunit.v3.generatorutility NuGet package. If your codebase has a home-grown [RetryFact], that sample is the shape of the work ahead.
A practical split that works: keep integration and end-to-end suites in reflection mode where diagnostics matter most, and move a fast, self-contained unit suite to AOT first as a pilot. Our QA and test automation practice runs this kind of staged cutover as a matter of course, and the ordering above is the part clients most often get wrong.
India-specific considerations
Two things change the calculus for teams building from India.
The first is agent and runner topology. Indian delivery teams frequently run self-hosted CI agents in a domestic region to keep source and test data inside the country, and self-hosted agents are exactly where a pinned SDK version hides. Analyzers 2.0.0 needs .NET SDK 8.0.400 or later, the new console tool needs the .NET 10 SDK, and an agent image built in 2024 and never rebuilt will fail on both. Audit the agent images before the packages.
The second is test data. Under the Digital Personal Data Protection Act 2023, production data copied into a test environment is still personal data, and parallel mode all multiplies the number of tests touching a shared fixture at the same time. Teams that relied on collection-level serialisation as an informal guard against concurrent access to a shared seeded database will lose that guard the moment they switch modes. If a fixture holds real or realistic personal data, disable parallelism at the collection level explicitly rather than depending on the old default.
The wider .NET modernisation sequencing, including the language-level changes arriving alongside these releases, is covered in our note on C# union types and the .NET 11 migration. For teams whose integration suites run against emulated cloud services, the same staging logic applies as in AWS serverless integration testing with LocalStack and SAM.
What the maintainer situation means for your risk assessment
One fact belongs in any dependency review. xUnit.net is maintained by Brad Wilson, who created it with James Newkirk, the original author of NUnit. Writing in July 2026, when Duende Software announced a sponsorship, Wilson said:
"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."
The project sits in the .NET Foundation under Apache 2.0, has over 4,500 GitHub stars and more than 27,400 dependent projects, and Wilson alone has made over 2,600 contributions to the repository. Duende's sponsorship is $250 per month for 12 months, totalling $3,000 for the year.
Read that as a bus-factor note, not a red flag. A release of this size, three coordinated packages with 20 new analyzers and a complete AOT code-generation stack, is evidence of a healthy project. It is also evidence that the project's throughput depends on a small number of people, which is a reasonable thing to weigh when you are choosing between staying on 3.2.2 for another year and moving now. Teams comparing options can read our broader view of software testing services and how tooling choices affect delivery risk.
FAQ
How eCorpIT can help
eCorpIT is a CMMI Level 5, MSME certified and ISO 27001:2022 certified engineering organisation in Gurugram, and our senior engineering teams run .NET runtime migrations and test-framework cutovers as staged programmes rather than single pull requests. We audit CI agent images and pipeline arguments before touching package versions, sequence the .NET 10, Microsoft Testing Platform v2 and xUnit.net 4.0 steps so failures stay isolated, and pilot Native AOT on one suite before committing a portfolio. If you are planning this work ahead of the 10 November 2026 support deadline, talk to us at /contact-us/.
References
Last updated: 19 August 2026.