eshop-full — testing strategy

186 tests, five projects deep — and the fix decorators finally let get proven.

MSTest.Sdk on .NET's newer Microsoft.Testing.Platform (MTP) runner — already pinned in global.json from the original SDK research, not a new choice. Testing isn't a batched end-of-migration phase: every project gets its own test project in the same unit of work as its source files — except Identity.API, whose ~129-file source tree deferred its test project until the whole .csproj was done, since per-file coverage at that scale isn't practical the way it is for a single-digit-to-low-teens-file project. See Event Flow for the Decorator split this testing approach depends on.

Coverage so far

ProjectSource files coveredTests
eShop.ServiceDefaults.UnitTests7 of 733
EventBusRabbitMQ.UnitTests7 of 7 applicable19
EventBus.UnitTests6 of 6 applicable16
IntegrationEventLogEF.UnitTests5 of 5 applicable17
Identity.API.UnitTests18 of 18 applicable101
Total186

"Applicable" means a file with a real contract to guard. Pure interfaces with zero behavior of their own (IEventBus, IEventBusBuilder) get skipped — but "simple" alone isn't the bar: a bare enum guarding persisted DB ordinals and a single-default-value POCO both get real tests elsewhere in this repo. Stated explicitly per file, not silently assumed clean.

Patterns worth carrying forward

Decorator-enabled testing

Fake inner IEventBus, no broker needed

ResilientEventBusDecorator/TelemetryEventBusDecorator wrap any IEventBus — so their tests use a fake inner bus instead of a real RabbitMQ broker (which doesn't exist yet, no eShop.AppHost).

Proof: PublishAsync_retries_and_succeeds_after_a_transient_SocketException verifies the Polly fix end-to-end for the first time — closing a gap open since the fix landed.

InternalsVisibleTo

Test internal classes directly, don't reflect

OpenApiOptionsExtensions is internal end-to-end. Rather than reach for reflection (brittle, tests implementation detail) or skip coverage of logic that had already shipped one real bug, the members got widened to internal and <InternalsVisibleTo> added — no public API change.

NSubstitute

Already centrally pinned (upstream's own Ordering.UnitTests uses it) — first real use in this fork.

Mock the pipeline, not the class under test

HttpClientExtensions's bearer-token-injecting handler is a private nested class — only reachable through AddAuthToken's public surface plus a real IServiceCollection and a mocked IAuthenticationService standing in for the ASP.NET Core auth pipeline.

Real ActivityListener

Don't mock OpenTelemetry, listen to it for real

RabbitMQTelemetryTests.cs and TelemetryEventBusDecoratorTests.cs both use a real ActivityListener/ActivitySource to confirm the actual messaging semantic-convention tags land with the right values — not a mock standing in for what OpenTelemetry would do.

CI, verified not assumed

MTP is a genuinely different CLI surface from legacy VSTest-based dotnet test — its own --help states plainly it "doesn't support VSTest," so the familiar --collect:"XPlat Code Coverage"/--logger trx flags don't apply. Every flag below was confirmed against a real scratch MSTest.Sdk project before trusting it in CI.

Coverage + TRX

--coverage --coverage-output-format cobertura and --report-trx are both built into the MTP runner itself — no extra NuGet package needed for either.

PR-visible reporting

dorny/test-reporter@v3 publishes the .trx as a PR-visible check run; a real push confirmed "3 passed, 0 failed" rendering with each test named, end-to-end against actual GitHub Actions.

i

Honest gap, tracked not solved: wanted one combined HTML report across every test project — MTP's --report-html produces one file per project instead (confirmed via a real 2-project scratch solution). The actual fix, microsoft/testfx#10529, merged 2026-08-09 but hasn't shipped in a Microsoft.Testing.Extensions.HtmlReport NuGet release yet. Dependabot already tracks that package individually, so no new tooling is needed to know when it does.