Summary
Implemented in #106: UnitTestEx.Aspire, a second, opt-in testing tier for genuine multi-host, inter-domain interactions (e.g. Shopping calling Products, both calling an external payment/mail gateway) via Aspire.Hosting.Testing.DistributedApplicationTestingBuilder - alongside, not replacing, today's intra-domain, single-host testing via WebApplicationFactory/ApiTesterBase.
See the README's Aspire multi-host testing section for full usage once merged.
What's delivered
- Two tiers, picked per test goal:
- Tier 1 (unchanged):
ApiTesterBase/WebApplicationFactory/MockHttpClientFactory - full in-process DI reach-in for deep, single-service isolation testing.
- Tier 2 (new):
UnitTestEx.Aspire (net8.0+) with AspireTester/AspireTesterBase<TAppHost, TSelf> wrapping DistributedApplicationTestingBuilder - real separate processes, real service discovery, for testing genuine interaction between two or more domains.
- Compile-time tier boundary: a new
TesterBaseCore holds only the genuinely host-agnostic members; Tier 1's TesterBase keeps its full DI-replacement surface on top of it, while Tier 2's AspireTesterBase inherits TesterBaseCore directly - DI-replacement methods simply don't exist on a Tier 2 tester, rather than throwing at runtime. A new IHttpClientSource interface lets the existing HttpTesterBase/HttpTester assertion API be reused unchanged by both tiers (ApiTesterBase sources its HttpClient from TestServer; AspireTesterBase sources it from the named Aspire resource).
HttpMock: a shared IHttpMockClient/IHttpMockRequest/IHttpMockResponse interface implemented by both Tier 1's MockHttpClient(Factory) and Tier 2's AspireHttpMockClient (backed by a real WireMock.Net resource) - the same fluent stubbing API, and the same YAML/JSON config schema (WithRequestsFromResourceAsync), works against either tier.
- External dependency mocking: UnitTestEx's recommended pattern is to self-host WireMock.Net as an ordinary Aspire project resource (Docker-free) rather than the official
WireMock.Net.Aspire package's container resource (which remains fully supported for teams already standardized on it). UnitTestEx.Aspire ships JsonElementComparerMatcher (a custom WireMock.Net matcher giving genuine JSON semantic-comparison parity with Tier 1) and WireMockConsole.RunAsync (collapses the self-hosted mock host's Program.cs boilerplate to one line).
- Per-component mocking across processes: no DI reach-in equivalent exists (as anticipated) - use environment/config-driven test hooks per project, resource swaps, or Tier 1 where deep DI control of one service is the actual goal.
Notes
- Playwright/UI-testing against Tier 2 resources needed no dedicated implementation or doc section - it is an ordinary consequence of Tier 2 resources being real, reachable URLs, not a UnitTestEx feature. A one-line footnote pointing to Microsoft's own Aspire + Playwright guide was added to the README instead.
Summary
Implemented in #106:
UnitTestEx.Aspire, a second, opt-in testing tier for genuine multi-host, inter-domain interactions (e.g. Shopping calling Products, both calling an external payment/mail gateway) viaAspire.Hosting.Testing.DistributedApplicationTestingBuilder- alongside, not replacing, today's intra-domain, single-host testing viaWebApplicationFactory/ApiTesterBase.See the README's Aspire multi-host testing section for full usage once merged.
What's delivered
ApiTesterBase/WebApplicationFactory/MockHttpClientFactory- full in-process DI reach-in for deep, single-service isolation testing.UnitTestEx.Aspire(net8.0+) withAspireTester/AspireTesterBase<TAppHost, TSelf>wrappingDistributedApplicationTestingBuilder- real separate processes, real service discovery, for testing genuine interaction between two or more domains.TesterBaseCoreholds only the genuinely host-agnostic members; Tier 1'sTesterBasekeeps its full DI-replacement surface on top of it, while Tier 2'sAspireTesterBaseinheritsTesterBaseCoredirectly - DI-replacement methods simply don't exist on a Tier 2 tester, rather than throwing at runtime. A newIHttpClientSourceinterface lets the existingHttpTesterBase/HttpTesterassertion API be reused unchanged by both tiers (ApiTesterBasesources itsHttpClientfromTestServer;AspireTesterBasesources it from the named Aspire resource).HttpMock: a sharedIHttpMockClient/IHttpMockRequest/IHttpMockResponseinterface implemented by both Tier 1'sMockHttpClient(Factory)and Tier 2'sAspireHttpMockClient(backed by a real WireMock.Net resource) - the same fluent stubbing API, and the same YAML/JSON config schema (WithRequestsFromResourceAsync), works against either tier.WireMock.Net.Aspirepackage's container resource (which remains fully supported for teams already standardized on it).UnitTestEx.AspireshipsJsonElementComparerMatcher(a custom WireMock.Net matcher giving genuine JSON semantic-comparison parity with Tier 1) andWireMockConsole.RunAsync(collapses the self-hosted mock host'sProgram.csboilerplate to one line).Notes