Skip to content

ADR-003: Testing Stack — xUnit v3, NSubstitute and Bogus

Date: 2026-02-21 Authors: Jean-Francois Meyers Scope: granit-dotnet, consuming applications

The Granit framework applies the “tests are part of the DoD” principle: each package has a *.Tests project and no code can be shipped without test coverage. The choice of testing stack is therefore foundational for the entire platform.

Requirements:

  • Test framework: parallelism, native CancellationToken, DI in tests, xUnit.v3 support for the new APIs
  • Mocking: dependency substitution (services, repositories, HTTP clients) with a clear API and no license issues
  • Test data: realistic and localized (FR) data generation
  • Coverage: code coverage collection for CI (Cobertura/OpenCover)
  • CI: result export in TRX/JUnit format for CI integration
RoleLibraryLicense
Test frameworkxUnit v3Apache-2.0
MockingNSubstituteBSD-3-Clause
Test dataBogusMIT
Coveragecoverlet.collectorMIT
CI reportJunitXml.TestLoggerMIT
  • Native CancellationToken (TestContext.Current.CancellationToken)
  • Parallelism by default (test collections)
  • Dominant adoption in the .NET open source ecosystem
  • Native IAsyncLifetime support for async setup/teardown
  • Mature framework, rich in attributes ([TestCase], [SetUp], [TearDown])
  • Disadvantage: less natural parallelism, modern .NET community leans more toward xUnit, no native CancellationToken in tests
  • Official Microsoft framework
  • Disadvantage: limited features compared to xUnit/NUnit, low adoption in .NET open source, less expressive API
  • Recent framework based on source generators (no runtime reflection)
  • Disadvantage: young project (v1.x), single maintainer, limited ecosystem (Testcontainers, Verify primarily target xUnit/NUnit)
  • Re-evaluation planned via a future ADR when the project reaches sufficient maturity (cf. ADR-014)
  • Clear and readable API (service.Method().Returns(value))
  • BSD-3-Clause — no license issues
  • No verbose Setup/Verify syntax
  • Most popular historical library
  • License issue: SponsorLink (v4.20+) injected telemetry code into builds, creating a compliance risk and a community trust crisis. Incompatible with GDPR/ISO 27001 security policy
  • Pleasant fluent API (A.CallTo(() => ...).Returns(...))
  • Disadvantage: more verbose syntax than NSubstitute, smaller community
  • Realistic data generation with locales (fr, fr_BE, en, etc.)
  • Fluent API (new Faker<T>().RuleFor(...))
  • Support for complex types and business rules
  • Automatic generation without configuration
  • Disadvantage: unrealistic data (random strings), less control over business values, less intuitive syntax
  • Port of Faker.js
  • Disadvantage: less rich API than Bogus, less maintained
CriterionxUnit v3NUnitMSTestTUnit
Native CancellationTokenYesNoNoYes
Default parallelismYesPartialPartialYes
.NET OSS adoptionDominantStrongLowEmerging
Maturity15+ years20+ years20+ years< 2 years
LicenseApache-2.0MITMITApache-2.0
CriterionNSubstituteMoqFakeItEasy
LicenseBSD-3-ClauseMIT (+ SponsorLink)Apache-2.0
SponsorLink riskNoYesNo
API concisenessExcellentGoodMedium
CommunityLargeVery largeMedium
CriterionBogusAutoFixtureFaker.NET
FR localesYes (fr, fr_BE)NoPartial
Realistic dataYesNo (random)Yes
Fluent APIYesPartialNo
Active maintenanceYesYesLow
  • Coherent and modern stack, adopted by the majority of the .NET ecosystem
  • Zero license risk (no SponsorLink, no commercial license)
  • Native CancellationToken xUnit v3: interruptible tests, faster CI
  • Realistic and localized test data (French names, SIRET, etc.)
  • Cobertura coverage for SonarQube/SonarCloud and CI
  • Migration from another test framework would be costly if necessary
  • xUnit v3 is recent: some third-party tools may have a support lag
  • Bogus generates pseudo-random data (non-deterministic by default — use Seed for reproducibility)