xUnit.net v3 4.0 shipped 14 August 2026: Native AOT, parallel mode all, and three support lines dropped

What xUnit.net v3 4.0 breaks, what it unlocks, and how to sequence the upgrade before 10 November 2026.

Read time
19 min
Word count
3.3K
Sections
13
FAQs
8
Share
xUnit.net v3 4.0 release summary: Native AOT, parallel mode all, dropped support lines
xUnit.net v3 4.0.0 shipped on 14 August 2026 alongside Analyzers 2.0.0 and the Visual Studio adapter 4.0.0.
On this page · 13 sections
  1. What actually shipped on 14 August 2026
  2. Native AOT: what you get and what you give up
  3. Parallel mode all, and the attribute that replaces CollectionBehavior
  4. The three support lines 4.0 drops
  5. The CI breakages that do not show up in code review
  6. Twenty new analyzer rules, and why most of them are AOT guard rails
  7. Sequencing the upgrade against 10 November 2026
  8. Should you move to Native AOT at all
  9. India-specific considerations
  10. What the maintainer situation means for your risk assessment
  11. FAQ
  12. How eCorpIT can help
  13. 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

  1. Core Framework v3 4.0.0 release notes, xUnit.net, 14 August 2026
  1. Analyzers 2.0.0 release notes, xUnit.net, 14 August 2026
  1. Visual Studio adapter 4.0.0 release notes, xUnit.net, 14 August 2026
  1. Testing with Native AOT, xUnit.net documentation
  1. Running Tests in Parallel, xUnit.net documentation
  1. Migrate from Microsoft.Testing.Platform v1 to v2, Microsoft Learn
  1. .NET 8 and .NET 9 will reach End of Support on November 10, 2026, .NET Blog
  1. .NET releases, patches, and support, Microsoft Learn
  1. xUnit.net open source sponsorship, Duende Software, 9 July 2026
  1. Update to GitHub Actions pricing, GitHub Changelog
  1. xunit/xunit issue 1986, full test parallelization
  1. xunit.v3.generatorutility package, NuGet
  1. Migrating from VSTest to Microsoft.Testing.Platform, Microsoft Learn

Last updated: 19 August 2026.

Frequently asked

Quick answers.

01 When was xUnit.net v3 4.0.0 released?
The xUnit.net project shipped Core Framework v3 4.0.0 on 14 August 2026, alongside Analyzers 2.0.0 and the Visual Studio adapter 4.0.0. It is the first major core framework release in 13 months, following 3.0.0, and arrives 7 months after the last minor release, version 3.2.2.
02 What does xUnit.net v3 4.0 stop supporting?
Version 4.0 discontinues official support for Microsoft Testing Platform v1 and for Mono. Analyzers 2.0.0 separately removes support for Visual Studio 2019, requiring Roslyn 4.11 or later, which means Visual Studio 2022 17.11 or .NET SDK 8.0.400 as the minimum build environment for analysis.
03 What is parallel mode all?
Parallel mode all is a third parallelisation setting introduced in 4.0 that runs every test in parallel against every other test, regardless of test collection or shared context. The default stays collections, so upgrading does not change concurrency until you opt in using the new [assembly: Parallelization] attribute.
04 Does Native AOT work with every .NET test project?
No. Native AOT mode supports C# only, because discovery relies on source generators inspecting C# source, and it requires .NET 9 as a minimum target framework. Generic test methods, serialization, EventSource reporting and interface-based attributes are all unavailable, and stack traces from published projects are significantly reduced.
05 Why do my CI test reports disappear after upgrading?
Default report file extensions changed in 4.0. The JUnit report moved from .junit to .junit.xml, NUnit from .nunit to .nunit.xml, and the xUnit.net v2 report from .xunit to .xunit.xml. Pipeline steps globbing for the old extensions collect nothing, and most publishers treat zero files as success.
06 Do I need a global.json to run tests on .NET 10?
Yes, if you use Microsoft Testing Platform v2. Microsoft's migration guide states that running MTP test projects on the .NET 10 SDK requires opting in to the MTP-based dotnet test through a global.json setting the runner to Microsoft.Testing.Platform. The old TestingPlatformDotnetTestSupport MSBuild property no longer enables this.
07 Which upgrade should come first, .NET 10 or xUnit.net 4.0?
Move the runtime first. .NET 8 and .NET 9 both reach end of support on 10 November 2026, which carries a security consequence that the framework upgrade does not. Adopt Microsoft Testing Platform v2 second, then take xUnit.net 4.0 in reflection mode, and evaluate Native AOT last.
08 Will parallel mode all actually cut CI cost?
Less than it would have in 2025. GitHub reduced GitHub-hosted runner prices by up to 39% on 1 January 2026, so each minute saved is worth correspondingly less, and public repository usage is free anyway. The real return on parallel mode all is a faster feedback loop for engineers, not a smaller cloud invoice.

About the author

Manu Shukla

Founder & Director

Founder of eCorpIT. Hands-on engineer leading senior-only delivery for AI apps, custom software, and cloud systems for global clients.

Subscribe

One engineering note a week. No fluff, no spam.

Senior-architect playbooks on AI agents, mobile apps, cloud, security, data, and marketing — delivered every Wednesday.

Past the reading

Read enough. Let's build something.

A senior architect responds in 24 working hours with scope, indicative cost, and a timeline. NDA before any technical conversation.