Skip to content

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

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

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

Conversation

@vbreuss

@vbreuss vbreuss commented Oct 18, 2026

Copy link
Copy Markdown
Member

Aligns aweXpect.Mockolate with aweXpect and Testably.Abstractions, which run their tests on TUnit and the Microsoft.Testing.Platform, so all test projects use one framework instead of xUnit v2 on .NET and xUnit v3 on net48.

Changes

  • TUnit for all tests: aweXpect.Mockolate.Tests and aweXpect.Mockolate.Api.Tests run on TUnit in source generation mode and share Tests/Directory.Build.props. xUnit, coverlet and Microsoft.NET.Test.Sdk are removed, and the Dependabot group for xUnit now groups the TUnit packages.
  • Failure types: without TUnit's assertions, a failed expectation throws aweXpect.FailException and a canceled verification TUnit's InconclusiveTestException, so the tests pin these types instead of XunitException and InconclusiveException.
  • Explicit API acceptance: AcceptApiChanges stays [Explicit], so neither a normal run nor CI executes it. It runs on purpose with dotnet test --project Tests/aweXpect.Mockolate.Api.Tests --treenode-filter "/*/*/ApiApprovalTests/AcceptApiChanges" (documented in CONTRIBUTING.md).
  • global.json opts into the Microsoft.Testing.Platform, so dotnet test runs the whole solution.
  • Pipeline: UnitTests and ApiChecks start each project and target framework with dotnet run and write TestResults/<project>_<tfm>.trx, plus .cobertura.xml for the unit tests, which CodeCoverage merges for Sonar. Stryker runs with --test-runner mtp on the unit and API tests. The unused xunit.runner.console download is removed.
  • Docs: CONTRIBUTING.md and the agent docs describe how to run and filter the tests, and the outdated names of the mutation targets and of the coverage report format are corrected.

Verification

  • Test counts are unchanged: 296 on net8.0 and net10.0, 241 on net48, and 3 API tests, with the same tests per method; dotnet test on the solution runs 836 tests and skips the explicit one. Five repeated runs on every target framework passed.
  • ./build.ps1 CodeCoverage ApiChecks writes the TRX and Cobertura files of every target framework and the SonarQube coverage report.
  • A temporary public API change fails all 3 API tests, and running AcceptApiChanges on purpose accepts it.
  • Stryker 5.0.0 on the whole library: the same 13 survived and 7 uncovered mutants as on main (score 93.87 % on both). 13 of the 14 mutants that timed out on main are now killed.

Aligns aweXpect.Mockolate with aweXpect and Testably.Abstractions, which run their tests on TUnit and the Microsoft.Testing.Platform, so all test projects use one framework instead of xUnit v2 on .NET and xUnit v3 on net48.

## Changes

- **TUnit for all tests**: `aweXpect.Mockolate.Tests` and `aweXpect.Mockolate.Api.Tests` run on TUnit in source generation mode and share `Tests/Directory.Build.props`. xUnit, coverlet and `Microsoft.NET.Test.Sdk` are removed, and the Dependabot group for xUnit now groups the TUnit packages.
- **Failure types**: without TUnit's assertions, a failed expectation throws `aweXpect.FailException` and a canceled verification TUnit's `InconclusiveTestException`, so the tests pin these types instead of `XunitException` and `InconclusiveException`.
- **Explicit API acceptance**: `AcceptApiChanges` stays `[Explicit]`, so neither a normal run nor CI executes it. It runs on purpose with `dotnet test --project Tests/aweXpect.Mockolate.Api.Tests --treenode-filter "/*/*/ApiApprovalTests/AcceptApiChanges"` (documented in CONTRIBUTING.md).
- **`global.json` opts into the Microsoft.Testing.Platform**, so `dotnet test` runs the whole solution.
- **Pipeline**: `UnitTests` and `ApiChecks` start each project and target framework with `dotnet run` and write `TestResults/<project>_<tfm>.trx`, plus `.cobertura.xml` for the unit tests, which `CodeCoverage` merges for Sonar. Stryker runs with `--test-runner mtp` on the unit and API tests. The unused `xunit.runner.console` download is removed.
- **Docs**: CONTRIBUTING.md and the agent docs describe how to run and filter the tests, and the outdated names of the mutation targets and of the coverage report format are corrected.

## Verification

- Test counts are unchanged: 296 on net8.0 and net10.0, 241 on net48, and 3 API tests, with the same tests per method; `dotnet test` on the solution runs 836 tests and skips the explicit one. Five repeated runs on every target framework passed.
- `./build.ps1 CodeCoverage ApiChecks` writes the TRX and Cobertura files of every target framework and the SonarQube coverage report.
- A temporary public API change fails all 3 API tests, and running `AcceptApiChanges` on purpose accepts it.
- Stryker 5.0.0 on the whole library: the same 13 survived and 7 uncovered mutants as on main (score 93.87 % on both). 13 of the 14 mutants that timed out on main are now killed.
@vbreuss vbreuss self-assigned this Oct 18, 2026
@vbreuss
vbreuss enabled auto-merge (squash) October 18, 2026 14:51
@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown

Test Results

    8 files  ±0      8 suites  ±0   1m 8s ⏱️ -1s
  299 tests  - 1    299 ✅ ±0  0 💤  - 1  0 ❌ ±0 
2 020 runs   - 1  2 020 ✅ ±0  0 💤  - 1  0 ❌ ±0 

Results for commit 1097f0e. ± Comparison against base commit c342e9e.

This pull request removes 163 and adds 162 tests. Note that renamed tests count towards both.
aweXpect.Mockolate.Api.Tests.ApiApprovalTests ‑ AcceptApiChanges
aweXpect.Mockolate.Api.Tests.ApiApprovalTests ‑ VerifyPublicApi(assemblyName: "aweXpect.Mockolate", framework: "net10.0")
aweXpect.Mockolate.Api.Tests.ApiApprovalTests ‑ VerifyPublicApi(assemblyName: "aweXpect.Mockolate", framework: "net8.0")
aweXpect.Mockolate.Api.Tests.ApiApprovalTests ‑ VerifyPublicApi(assemblyName: "aweXpect.Mockolate", framework: "netstandard2.0")
aweXpect.Mockolate.Tests.ThatVerificationResultIs+AtLeastOnceTests ‑ WhenInvokedInBackground_WithCancellation_ShouldSucceed(invocationTimes: 1)
aweXpect.Mockolate.Tests.ThatVerificationResultIs+AtLeastOnceTests ‑ WhenInvokedInBackground_WithCancellation_ShouldSucceed(invocationTimes: 7)
aweXpect.Mockolate.Tests.ThatVerificationResultIs+AtLeastOnceTests ‑ WhenInvokedInBackground_WithTimeout_ShouldSucceed(invocationTimes: 1)
aweXpect.Mockolate.Tests.ThatVerificationResultIs+AtLeastOnceTests ‑ WhenInvokedInBackground_WithTimeout_ShouldSucceed(invocationTimes: 7)
aweXpect.Mockolate.Tests.ThatVerificationResultIs+AtLeastOnceTests ‑ WhenInvokedInBackground_Within_ShouldSucceed(invocationTimes: 1)
aweXpect.Mockolate.Tests.ThatVerificationResultIs+AtLeastOnceTests ‑ WhenInvokedInBackground_Within_ShouldSucceed(invocationTimes: 7)
…
aweXpect.Mockolate.Api.Tests.ApiApprovalTests ‑ VerifyPublicApi(aweXpect·Mockolate, net10·0)
aweXpect.Mockolate.Api.Tests.ApiApprovalTests ‑ VerifyPublicApi(aweXpect·Mockolate, net8·0)
aweXpect.Mockolate.Api.Tests.ApiApprovalTests ‑ VerifyPublicApi(aweXpect·Mockolate, netstandard2·0)
aweXpect.Mockolate.Tests.ThatVerificationResultIs+AtLeastOnceTests ‑ WhenInvokedInBackground_WithCancellation_ShouldSucceed(1)
aweXpect.Mockolate.Tests.ThatVerificationResultIs+AtLeastOnceTests ‑ WhenInvokedInBackground_WithCancellation_ShouldSucceed(7)
aweXpect.Mockolate.Tests.ThatVerificationResultIs+AtLeastOnceTests ‑ WhenInvokedInBackground_WithTimeout_ShouldSucceed(1)
aweXpect.Mockolate.Tests.ThatVerificationResultIs+AtLeastOnceTests ‑ WhenInvokedInBackground_WithTimeout_ShouldSucceed(7)
aweXpect.Mockolate.Tests.ThatVerificationResultIs+AtLeastOnceTests ‑ WhenInvokedInBackground_Within_ShouldSucceed(1)
aweXpect.Mockolate.Tests.ThatVerificationResultIs+AtLeastOnceTests ‑ WhenInvokedInBackground_Within_ShouldSucceed(7)
aweXpect.Mockolate.Tests.ThatVerificationResultIs+AtLeastTests ‑ WhenInvokedAtLeastTheSameTimes_ShouldSucceed(18)
…

@vbreuss
vbreuss merged commit 96dfd26 into main Oct 18, 2026
16 checks passed
@vbreuss
vbreuss deleted the test/migrate-to-tunit branch October 18, 2026 16:59
@github-actions

Copy link
Copy Markdown

🚀 Benchmark Results

Details

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.5 LTS (Noble Numbat)
AMD EPYC 7763 3.24GHz, 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
DefaultJob : .NET 10.0.13 (10.0.13, 10.0.1326.50504), X64 RyuJIT x86-64-v3

Method Mean Error StdDev Median Allocated
Dummy_aweXpect 0.0003 ns 0.0010 ns 0.0009 ns 0.0 ns -

@github-actions

Copy link
Copy Markdown

👽 Mutation Results

Mutation testing badge

aweXpect.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

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.

1 participant