Fix flaky CLI tests: serialize Program.Services mutators with [Collection]#222
Conversation
AppleCommandsTests, AndroidCommandsTests and ServiceConfigurationTests
all mutate the static Program.Services to inject fake providers. Without
[Collection] they parallelized against each other and against the
existing [Collection(""CLI"")] DevFlow tests, so a concurrent test would
overwrite Program.Services mid-test and the action under test would
resolve a different (or the production) provider.
Concrete symptom on macOS CI: AppleCommandsTests
.InstallCommand_Json_ReturnsOneForFailedStatus saw exitCode=0 instead of
1 because Program.AppleProvider had been replaced by a different fake
(or the real provider) by the time the install handler ran.
Fix: tag all three classes with [Collection(""CLI"")] so they share the
DisableParallelization-flagged collection that already exists in
Fixtures/CliCollection.cs.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR addresses flaky CLI unit tests by ensuring test classes that mutate the process-wide static Program.Services run serially. It does so by adding the existing xUnit [Collection("CLI")] attribute (backed by DisableParallelization = true) to the affected test classes; no production code changes are included.
Changes:
- Add
[Collection("CLI")]toAppleCommandsTeststo prevent concurrentProgram.Servicesoverrides. - Add
[Collection("CLI")]toAndroidCommandsTeststo prevent concurrentProgram.Servicesoverrides. - Add
[Collection("CLI")]toServiceConfigurationTeststo prevent concurrentProgram.Servicesoverrides.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| src/Cli/Microsoft.Maui.Cli.UnitTests/AppleCommandsTests.cs | Serializes execution with other CLI tests to avoid cross-test DI contamination via Program.Services. |
| src/Cli/Microsoft.Maui.Cli.UnitTests/AndroidCommandsTests.cs | Serializes execution with other CLI tests to avoid cross-test DI contamination via Program.Services. |
| src/Cli/Microsoft.Maui.Cli.UnitTests/ServiceConfigurationTests.cs | Serializes execution with other CLI tests to avoid cross-test DI contamination via Program.Services. |
Expert Code Review — PR #222Methodology: 3 independent reviewers with adversarial consensus Result: ✅ The fix is correct. All 3 reviewers independently confirmed the Findings: 1 minor (outside diff)All consensus findings are outside the changed diff hunks and cannot be posted inline:
Discarded findings (did not reach consensus)
CI Status
Test CoverageThe PR modifies only test infrastructure (adding
|
AppleCommandsTests, AndroidCommandsTests and ServiceConfigurationTests
all mutate the static Program.Services to inject fake providers. Without
[Collection] they parallelized against each other and against the
existing [Collection(""CLI"")] DevFlow tests, so a concurrent test would
overwrite Program.Services mid-test and the action under test would
resolve a different (or the production) provider.
Concrete symptom on macOS CI: AppleCommandsTests
.InstallCommand_Json_ReturnsOneForFailedStatus saw exitCode=0 instead of
1 because Program.AppleProvider had been replaced by a different fake
(or the real provider) by the time the install handler ran.
Fix: tag all three classes with [Collection(""CLI"")] so they share the
DisableParallelization-flagged collection that already exists in
Fixtures/CliCollection.cs.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: jfversluis <939291+jfversluis@users.noreply.github.com>
Why
Three CLI test classes —
AppleCommandsTests,AndroidCommandsTests,ServiceConfigurationTests— mutate the staticProgram.Servicesto inject fake providers, but were not part of any xUnit[Collection]. They therefore parallelized against each other and against the existing[Collection("CLI")]DevFlow tests, which also mutateProgram.Services.When two such tests ran concurrently, one would overwrite
Program.Servicesmid-test, so the action under test resolved a different (or the production) provider. Thetry { Program.Services = testProvider; ... } finally { Program.ResetServices(); }pattern is not safe for parallel execution becauseProgram.Servicesis process-wide.Concrete symptom
On macOS CI for PR #202:
The install handler in
AppleCommands.cscorrectly returns1forStatus = "failed":…but
Program.AppleProviderhad been replaced by a parallel test's fake (whose defaultInstallResult.Status = "ok"), so the handler saw"ok"and returned0.This also explains why the failure was intermittent: PR #200's CI run happened to win the race; PR #202's lost it.
Fix
Tag all three classes with
[Collection("CLI")]so they share theDisableParallelization = truecollection already defined inFixtures/CliCollection.cs. No production code changes.Verification
dotnet testover the affected classes — 35/35 pass (AppleCommandsTestsInstall* skip on non-macOS, expected).Follow-up
Unblocks CI on PR #200 and PR #202.