Build1 distinct publisher3 min readUpdated
Full test-case parallelization stays off by default in xUnit.net v3 4.0.0. The assembly attribute that turns it on also withdraws the guarantee that two rows of one theory will not overlap.
The Engineer · Build desk
Compiled by The EngineerSomething wrong?How this is made
xUnit.net v3 4.0.0 lists full test-case parallelization as a new feature, and the default remains ParallelMode.Collections, so an upgrade does not silently change how your suite schedules itself [1][2]. The consequence sits in the opt-in: switching to ParallelMode.All does not just add throughput, it withdraws the assumption that two tests in the same class, or two pre-enumerated rows of the same theory, will never overlap [3].
Mechanically the change is three lines. You declare it at the assembly level, for example `[assembly: Parallelization(Mode = ParallelMode.All, MaxThreads = 2, Algorithm = ParallelAlgorithm.Conservative)]`, importing Xunit.Sdk and Xunit.v3 [4]. Under Collections, tests within a collection are serialized; under All, every test case is eligible to run beside every other one [3]. The official parallel test execution guide documents the modes, the algorithms, and the opt-out scopes available [20].
The dev.to write-up by ssukhpinder frames this as an isolation change rather than a speed switch, and that framing does real work [5]. A static fake, a shared fixture, a temporary file with a fixed name, or a database record addressed by a shared ID was safe when the collection serialized its members; it becomes a race the moment the mode flips [6]. The pre-flip inventory he recommends is concrete: mutable static fields, IClassFixture and ICollectionFixture implementations, fixed file names, environment-variable mutation, test servers bound to fixed ports, records keyed by shared IDs, and theory data sources holding objects that rows can mutate [7]. Each item then resolves one of three ways: make the resource concurrency-safe, give it a unique per-test identity, or keep it behind an explicit opt-out. Doing that before a broad CI failure mixes several races into one red build is the cheaper order of operations [8].
Proving the risk needs determinism, not luck. A timing-only test built on Task.Delay can pass for the wrong reason, so the sample uses a Barrier with two participants to force the interleaving: two theory rows read one static counter before either writes, both write afterward, and both then assert the counter equals 2 [10][11]. Both rows observe 0 and both write 1, so the final value is 1 and one increment is lost [11][19]. The barriers make that lost update repeatable on an idle machine, and a ten-second timeout stops a misconfigured run from hanging [12]. The test is deliberately red and lives in a separate project, so the failure is evidence from a verifier rather than a permanent red mark in the guarded suite [13].
The fixes split along the same line. Interlocked.Increment makes a counter atomic, while a lock or a concurrent collection suits a compound invariant, but thread safety is not isolation: two tests can mutate a structure correctly and still see each other's logical data [14][15]. For an exclusive resource, `[Theory(DisableParallelization = true)]` is the targeted opt-out [16]. The sample does not take that attribute on trust. A control configuration compiles the same method without it and uses two barriers to make both rows attempt a one-at-a-time lease before either releases; exactly one row fails, and the normal Release build restores the opt-out so both pass [17].
MaxThreads = 2 in the sample is an inspection aid, not a CI setting; the right number depends on CPU, memory, and the external systems the tests touch [9]. The sample also pins xunit.v3.mtp-v2 at 4.0.0 and selects Microsoft Testing Platform in global.json [18].
Follow any of these and your For You feed starts watching them — no settings page required.
Ranked by verification strength, evidence, and original report placement.
The xUnit.net v3 4.0.0 release notes describe full test-case parallelization as a new feature.
The default parallelization mode is still ParallelMode.Collections, so upgrading does not silently enable the broader mode.
With ParallelMode.Collections, tests within a collection are serialized. With ParallelMode.All, every test case is eligible to run beside every other test case, including two cases from the same class and two pre-enumerated rows from the same theory.
Opting in is done at the assembly level, using Xunit.Sdk and Xunit.v3: [assembly: Parallelization(Mode = ParallelMode.All, MaxThreads = 2, Algorithm = ParallelAlgorithm.Conservative)].
The author treats the switch as an isolation change, not a speed switch, and wants a deterministic failure that proves the risk plus a deterministic check for each guardrail before enabling it across a suite.
A static fake, shared fixture, temporary file, or database record that was safe under collection-level parallelism can become a race under full test-case parallelization.
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Self-contained code evidence, single unverified source
The mechanism claims are unusually concrete for a single post: the mode semantics, the assembly attribute, a barrier-gated theory whose failure is deterministic rather than timing-dependent, an Interlocked remediation, and a named per-theory opt-out are all shown in code inside the article. What is missing is anything outside the author's own text - the v3 4.0.0 release notes, the official parallel execution guide, and the sample pull request with its control build are cited but not supplied, so the release behaviour and the 'exactly one row fails' control result cannot be checked. One publisher, one author, no logs or measurements.
Fresh release plus one demo project
Adoption evidence is limited to two observations: the xUnit.net v3 4.0.0 release itself, in which full test-case parallelization is opt-in and off by default, and the author's own sample pinning xunit.v3.mtp-v2 at 4.0.0 on Microsoft Testing Platform. No suite migrations, CI results, download figures, or third-party usage of ParallelMode.All appear anywhere in the supplied material, and the author himself recommends keeping the default for suites with heavy shared state.
Deliberately deflated relative to the flag's framing
The article's headline claim - that ParallelMode.All is an isolation change rather than a performance switch - is matched by the evidence it presents, and the author repeatedly removes lift he could have taken: MaxThreads = 2 is disclaimed as a demonstration value, the barrier sample is explicitly declared not a performance claim, the red test is quarantined in a separate project, and the recommended default for shared-state-heavy suites is to leave the mode alone. The mild negative reflects that a real upgrade hazard is described in cooler terms than release-note framing of a 'new parallelization feature' would suggest, offset slightly by the sample pull request's control result being asserted rather than shown.
Practitioner audience-building, no disclosed commercial tie
The visible incentive is developer-audience building on a personal publishing platform: a first-person walkthrough that ends with an engagement prompt asking which shared resource readers would audit first, plus a sign-off. No vendor sponsorship, employer interest, product being sold, or affiliation with the xUnit project is disclosed or evident, and the post steers readers toward the conservative default rather than toward a purchase or a dependency. The residual score reflects the reputational incentive to present a tidy sample pull request whose control-build outcome the reader cannot independently verify.
Mechanism credible, breadth unestablished
Confidence is moderate. The internal logic is coherent and the code-level claims are the kind a reader can reproduce, which supports the mechanism. But the cluster has one publisher, the cited release notes and official guide are absent, the control-build result is asserted, and there is no adoption or performance data at all - so the semantics of ParallelMode.All are well described while its practical impact on real suites remains unmeasured in this material.
build
MTP 2.3 writes TRX as tests finish, so a dead test host no longer erases the evidence1 distinct publisher
build
One non-ASCII letter broke four toolchains: path checks belong in CI, not folklore1 distinct publisher
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 17, 2026