Skip to content

test: migrate the tests from xUnit to TUnit - #726

Merged
vbreuss merged 1 commit into
mainfrom
topic/migrate-to-tunit
Oct 18, 2026
Merged

vbreuss merged 1 commit into
mainfrom
topic/migrate-to-tunit

Conversation

@vbreuss

@vbreuss vbreuss commented Apr 30, 2026 •

Copy link
Copy Markdown
Member

Aligns Mockolate with aweXpect and Testably.Abstractions, which run their tests on TUnit and the Microsoft.Testing.Platform, so all test projects use one framework.

Changes

  • TUnit for all tests: Mockolate.Tests, Mockolate.Internal.Tests, Mockolate.SourceGenerators.Tests, Mockolate.Analyzers.Tests, Mockolate.ExampleTests, Mockolate.Api.Tests and Build.Tests run on TUnit in reflection mode and share Tests/Directory.Build.props. [Fact]/[Theory] become [Test], [InlineData] [Arguments], [MemberData]/TheoryData [MethodDataSource], xUnit's TestContext.Current.CancellationToken an injected CancellationToken, and Record.Exception a small Catch.Exception helper. xUnit, coverlet and Microsoft.NET.Test.Sdk are removed.
  • Explicit acceptance tests: AcceptApiChanges and AcceptSnapshotChanges stay [Explicit], so neither a normal run nor CI executes them. They run on purpose with a tree node filter that selects only them.
  • global.json opts into the Microsoft.Testing.Platform, so dotnet test runs the whole solution.
  • Pipeline: UnitTests and ApiChecks run each project and target framework through dotnet test and write TestResults/<project>_<tfm>.trx, plus .cobertura.xml from Microsoft.Testing.Extensions.CodeCoverage for the unit tests, which ReportGenerator turns into the SonarQube report as before. Stryker runs with --test-runner mtp. The unused xunit.runner.console and coverlet.console downloads are removed, and Dependabot groups TUnit* instead of xunit*.
  • Docs: the agent guide and Docs/agents/workflows.md describe how to run and filter the tests and the acceptance tests, and outdated statements about rollForward, the coverage report format and the mutation test targets are corrected.

Verification

  • Test counts are unchanged per class on every target framework: Mockolate.Tests 3,419 (net10.0) / 3,288 (net8.0) / 3,057 (net48), Mockolate.Internal.Tests 582 / 564 / 564, Mockolate.ExampleTests 19 / 19 / 14, Mockolate.SourceGenerators.Tests 345, Mockolate.Analyzers.Tests 96, Build.Tests 58, Mockolate.Api.Tests 3. xUnit listed the two explicit tests as not executed; TUnit leaves them out. dotnet test --solution Mockolate.slnx runs 12,028 tests, all passing, and four more runs of Mockolate.Tests and Mockolate.Internal.Tests on all three frameworks passed as well.
  • A temporary public API change fails the API test, and the acceptance tests run on purpose with their filter.
  • CodeCoverage and ApiChecks succeed locally and write the TRX, Cobertura and SonarQube files; line coverage is 93.3% as on main.
  • Stryker 5.0.0 on Source/Mockolate/Verify (net8.0, mutation level Advanced): 3,875 tests found and 303 mutants tested on both sides; main 260 killed, 26 survived, 10 timeout, 7 no coverage (89.11%, 12 min); this branch 261 killed, 27 survived, 8 timeout, 7 no coverage (88.78%, 13 min).

@vbreuss vbreuss self-assigned this Apr 30, 2026
@github-actions

github-actions Bot commented Apr 30, 2026 •

Copy link
Copy Markdown

Test Results

    24 files      24 suites   30m 5s ⏱️
 4 514 tests  4 514 ✅ 0 💤 0 ❌
28 680 runs  28 680 ✅ 0 💤 0 ❌

Results for commit ae2e2ae.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Apr 30, 2026 •

Copy link
Copy Markdown

🚀 Benchmark Results

Details

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.5 LTS (Noble Numbat)
AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.303
[Host] : .NET 10.0.13 (10.0.13, 10.0.1326.50504), X64 RyuJIT x86-64-v3

Job=InProcess Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=1 WarmupCount=10

Method N Mean Error StdDev Ratio Allocated Alloc Ratio
baseline* 1 291.2 ns 8.78 ns 8.21 ns 0.83 1.93 KB 1.00
Mockolate 1 350.6 ns 3.11 ns 2.43 ns 1.00 1.93 KB 1.00
Imposter 1 548.6 ns 2.29 ns 2.03 ns 1.56 3.96 KB 2.05
TUnitMocks 1 487.5 ns 4.27 ns 3.99 ns 1.39 2.02 KB 1.05
Moq 1 183,381.6 ns 647.71 ns 574.17 ns 523.12 14.58 KB 7.55
NSubstitute 1 6,188.3 ns 18.10 ns 16.93 ns 17.65 9.06 KB 4.70
FakeItEasy 1 6,112.2 ns 21.06 ns 17.59 ns 17.44 8.05 KB 4.17
baseline* 10 535.8 ns 5.49 ns 5.14 ns 0.80 2.14 KB 1.00
Mockolate 10 671.2 ns 3.13 ns 2.93 ns 1.00 2.14 KB 1.00
Imposter 10 885.7 ns 4.72 ns 4.41 ns 1.32 4.8 KB 2.24
TUnitMocks 10 1,374.4 ns 12.47 ns 11.66 ns 2.05 3.73 KB 1.74
Moq 10 188,470.0 ns 586.10 ns 548.24 ns 280.81 18.48 KB 8.63
NSubstitute 10 9,085.7 ns 21.93 ns 19.44 ns 13.54 11.52 KB 5.38
FakeItEasy 10 9,709.6 ns 26.95 ns 25.21 ns 14.47 15.42 KB 7.20
Details

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.5 LTS (Noble Numbat)
INTEL XEON PLATINUM 8573C 3.07GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.303
[Host] : .NET 10.0.13 (10.0.13, 10.0.1326.50504), X64 RyuJIT x86-64-v4

Job=InProcess Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=1 WarmupCount=10

Event Mean Error StdDev Ratio Allocated Alloc Ratio
baseline* 236.4 ns 2.00 ns 1.87 ns 0.64 1.7 KB 1.00
Mockolate 369.7 ns 4.47 ns 3.96 ns 1.00 1.7 KB 1.00
Imposter 1,054.8 ns 10.02 ns 8.89 ns 2.85 7.18 KB 4.22
TUnitMocks 253.5 ns 0.74 ns 0.66 ns 0.69 1.34 KB 0.79
Moq 13,979.0 ns 67.23 ns 56.14 ns 37.81 12.51 KB 7.34
NSubstitute 5,289.4 ns 8.54 ns 6.67 ns 14.31 9.05 KB 5.31
FakeItEasy 124,430.0 ns 1,207.25 ns 1,129.26 ns 336.59 15.22 KB 8.94
Details

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.5 LTS (Noble Numbat)
AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.303
[Host] : .NET 10.0.13 (10.0.13, 10.0.1326.50504), X64 RyuJIT x86-64-v3

Job=InProcess Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=1 WarmupCount=10

Callback Mean Error StdDev Ratio Allocated Alloc Ratio
baseline* 362.1 ns 8.31 ns 7.77 ns 1.13 1.57 KB 1.00
Mockolate 320.9 ns 12.74 ns 11.92 ns 1.00 1.57 KB 1.00
Imposter 446.0 ns 12.06 ns 11.28 ns 1.39 2.37 KB 1.51
TUnitMocks 501.2 ns 19.65 ns 18.38 ns 1.56 2 KB 1.27
Moq 99,512.1 ns 590.82 ns 552.65 ns 310.46 8.88 KB 5.66
NSubstitute 4,727.0 ns 28.84 ns 25.57 ns 14.75 7.71 KB 4.91
FakeItEasy 5,214.9 ns 33.95 ns 31.76 ns 16.27 6.81 KB 4.33
Details

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.5 LTS (Noble Numbat)
INTEL XEON PLATINUM 8573C 2.30GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.303
[Host] : .NET 10.0.13 (10.0.13, 10.0.1326.50504), X64 RyuJIT x86-64-v4

Job=InProcess Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=1 WarmupCount=10

Indexer N Mean Error StdDev Ratio Allocated Alloc Ratio
baseline* 1 1,058.5 ns 26.48 ns 23.47 ns 1.27 3.77 KB 1.00
Mockolate 1 836.4 ns 19.78 ns 18.51 ns 1.00 3.77 KB 1.00
Imposter 1 679.8 ns 12.84 ns 12.01 ns 0.81 4.99 KB 1.33
Moq 1 97,812.0 ns 1,113.95 ns 1,041.99 ns 117.00 20.38 KB 5.41
NSubstitute 1 9,130.3 ns 182.27 ns 161.57 ns 10.92 12.78 KB 3.39
FakeItEasy 1 7,636.0 ns 106.23 ns 99.37 ns 9.13 13.62 KB 3.62
baseline* 10 2,596.8 ns 17.14 ns 16.03 ns 1.02 4.82 KB 1.00
Mockolate 10 2,552.3 ns 34.94 ns 30.97 ns 1.00 4.82 KB 1.00
Imposter 10 1,253.8 ns 24.49 ns 20.45 ns 0.49 6.47 KB 1.34
Moq 10 105,089.7 ns 1,391.75 ns 1,301.85 ns 41.18 29.88 KB 6.20
NSubstitute 10 21,586.7 ns 234.36 ns 219.22 ns 8.46 25.58 KB 5.31
FakeItEasy 10 18,605.2 ns 154.49 ns 136.95 ns 7.29 32.98 KB 6.84
Details

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.5 LTS (Noble Numbat)
AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.303
[Host] : .NET 10.0.13 (10.0.13, 10.0.1326.50504), X64 RyuJIT x86-64-v3

Job=InProcess Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=1 WarmupCount=10

CreateMock Mean Error StdDev Ratio Allocated Alloc Ratio
baseline* 9.887 ns 0.1868 ns 0.1656 ns 0.57 160 B 1.00
Mockolate 17.38 ns 0.191 ns 0.179 ns 1.00 160 B 1.00
Imposter 272.81 ns 2.272 ns 2.125 ns 15.70 2240 B 14.00
TUnitMocks 41.32 ns 0.367 ns 0.344 ns 2.38 200 B 1.25
Moq 1,393.07 ns 4.821 ns 4.025 ns 80.18 2096 B 13.10
NSubstitute 1,987.88 ns 17.154 ns 16.046 ns 114.42 5048 B 31.55
FakeItEasy 1,794.32 ns 13.349 ns 12.486 ns 103.28 2763 B 17.27
Details

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.5 LTS (Noble Numbat)
Intel Xeon 6973P-C 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.303
[Host] : .NET 10.0.13 (10.0.13, 10.0.1326.50504), X64 RyuJIT x86-64-v4

Job=InProcess Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=1 WarmupCount=10

Property N Mean Error StdDev Ratio Allocated Alloc Ratio
baseline* 1 531.7 ns 21.36 ns 19.98 ns 1.27 2.41 KB 1.00
Mockolate 1 418.3 ns 9.80 ns 9.17 ns 1.00 2.41 KB 1.00
Imposter 1 320.0 ns 14.50 ns 13.56 ns 0.77 2.98 KB 1.23
TUnitMocks 1 362.3 ns 18.29 ns 16.21 ns 0.87 1.64 KB 0.68
Moq 1 6,675.5 ns 92.93 ns 86.92 ns 15.97 10.39 KB 4.30
NSubstitute 1 4,492.2 ns 61.41 ns 57.45 ns 10.74 11.45 KB 4.74
FakeItEasy 1 4,785.4 ns 205.74 ns 192.45 ns 11.45 11.23 KB 4.65
baseline* 10 1,008.5 ns 5.49 ns 4.86 ns 1.13 2.91 KB 1.00
Mockolate 10 894.0 ns 51.84 ns 48.49 ns 1.00 2.91 KB 1.00
Imposter 10 480.6 ns 11.15 ns 10.43 ns 0.54 3.26 KB 1.12
TUnitMocks 10 1,393.9 ns 95.77 ns 89.58 ns 1.56 3.94 KB 1.35
Moq 10 11,241.5 ns 65.57 ns 51.19 ns 12.61 18.28 KB 6.29
NSubstitute 10 10,395.4 ns 170.04 ns 132.75 ns 11.66 21.08 KB 7.25
FakeItEasy 10 11,351.8 ns 598.17 ns 530.26 ns 12.73 30.8 KB 10.60

baseline* rows show the corresponding Mockolate benchmark from the most recent successful main branch build with results, for regression comparison.

@vbreuss
vbreuss force-pushed the topic/migrate-to-tunit branch from 74ff11c to ef8a9f7 Compare October 15, 2026 18:34
Aligns Mockolate with aweXpect and Testably.Abstractions, which run their tests on TUnit and the Microsoft.Testing.Platform, so all test projects use one framework.

## Changes

- **TUnit for all tests**: `Mockolate.Tests`, `Mockolate.Internal.Tests`, `Mockolate.SourceGenerators.Tests`, `Mockolate.Analyzers.Tests`, `Mockolate.ExampleTests`, `Mockolate.Api.Tests` and `Build.Tests` run on TUnit in reflection mode and share `Tests/Directory.Build.props`. `[Fact]`/`[Theory]` become `[Test]`, `[InlineData]` `[Arguments]`, `[MemberData]`/`TheoryData` `[MethodDataSource]`, xUnit's `TestContext.Current.CancellationToken` an injected `CancellationToken`, and `Record.Exception` a small `Catch.Exception` helper. xUnit, coverlet and `Microsoft.NET.Test.Sdk` are removed.
- **Explicit acceptance tests**: `AcceptApiChanges` and `AcceptSnapshotChanges` stay `[Explicit]`, so neither a normal run nor CI executes them. They run on purpose with a tree node filter that selects only them.
- **`global.json` opts into the Microsoft.Testing.Platform**, so `dotnet test` runs the whole solution.
- **Pipeline**: `UnitTests` and `ApiChecks` run each project and target framework through `dotnet test` and write `TestResults/<project>_<tfm>.trx`, plus `.cobertura.xml` from Microsoft.Testing.Extensions.CodeCoverage for the unit tests, which ReportGenerator turns into the SonarQube report as before. Stryker runs with `--test-runner mtp`. The unused `xunit.runner.console` and `coverlet.console` downloads are removed, and Dependabot groups `TUnit*` instead of `xunit*`.
- **Docs**: the agent guide and `Docs/agents/workflows.md` describe how to run and filter the tests and the acceptance tests, and outdated statements about `rollForward`, the coverage report format and the mutation test targets are corrected.

## Verification

- Test counts are unchanged per class on every target framework: `Mockolate.Tests` 3,419 (net10.0) / 3,288 (net8.0) / 3,057 (net48), `Mockolate.Internal.Tests` 582 / 564 / 564, `Mockolate.ExampleTests` 19 / 19 / 14, `Mockolate.SourceGenerators.Tests` 345, `Mockolate.Analyzers.Tests` 96, `Build.Tests` 58, `Mockolate.Api.Tests` 3. xUnit listed the two explicit tests as not executed; TUnit leaves them out. `dotnet test --solution Mockolate.slnx` runs 12,028 tests, all passing, and four more runs of `Mockolate.Tests` and `Mockolate.Internal.Tests` on all three frameworks passed as well.
- A temporary public API change fails the API test, and the acceptance tests run on purpose with their filter.
- `CodeCoverage` and `ApiChecks` succeed locally and write the TRX, Cobertura and SonarQube files; line coverage is 93.3% as on main.
- Stryker 5.0.0 on `Source/Mockolate/Verify` (net8.0, mutation level Advanced): 3,875 tests found and 303 mutants tested on both sides; main 260 killed, 26 survived, 10 timeout, 7 no coverage (89.11%, 12 min); this branch 261 killed, 27 survived, 8 timeout, 7 no coverage (88.78%, 13 min).
@vbreuss
vbreuss force-pushed the topic/migrate-to-tunit branch from ef8a9f7 to ae2e2ae Compare October 18, 2026 12:58
@vbreuss
vbreuss marked this pull request as ready for review October 18, 2026 13:03
Copilot AI balanced review requested due to automatic review settings October 18, 2026 13:03

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@vbreuss
vbreuss enabled auto-merge (squash) October 18, 2026 13:03
@vbreuss
vbreuss disabled auto-merge October 18, 2026 13:03
@vbreuss
vbreuss enabled auto-merge (squash) October 18, 2026 13:03
@sonarqubecloud

Copy link
Copy Markdown

@vbreuss vbreuss changed the title refactor: migrate to TUnit test: migrate the tests from xUnit to TUnit Oct 18, 2026
@vbreuss
vbreuss disabled auto-merge October 18, 2026 15:30
@vbreuss
vbreuss enabled auto-merge (squash) October 18, 2026 15:30
@vbreuss
vbreuss merged commit f28514f into main Oct 18, 2026
19 checks passed
@vbreuss
vbreuss deleted the topic/migrate-to-tunit branch October 18, 2026 15:46
@github-actions

Copy link
Copy Markdown

👽 Mutation Results

Mutation testing badge

Mockolate

Details
File Score Killed Survived Timeout No Coverage Ignored Compile Errors Runtime Errors Total Detected Total Undetected Total Mutants

The final mutation score is NaN%

Coverage Thresholds: high:80 low:60 break:0

github-actions Bot added a commit that referenced this pull request Oct 18, 2026
github-actions Bot added a commit that referenced this pull request Oct 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants