Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
47de960
docs: add design note on Aspire multi-host testing support
chullybun Sep 22, 2026
cbe9363
docs: address TesterBase<TSelf> capability gap for Aspire testers
chullybun Sep 22, 2026
b87c11b
docs: make SupportsServiceConfiguration public, audit existing extens…
chullybun Sep 22, 2026
1f796fb
feat: add TesterBase.SupportsServiceConfiguration guard (Phase 1 of #…
chullybun Sep 22, 2026
26a3d18
fix: address Copilot review feedback on PR #106
chullybun Sep 22, 2026
320c975
docs: revise design doc - AspireTesterBase inherits TesterBase, not T…
chullybun Sep 22, 2026
707b3f3
Revert SupportsServiceConfiguration guard; adopt TesterBaseCore split…
chullybun Sep 22, 2026
ce2e11d
feat: extract TesterBaseCore for host-agnostic testing capabilities (…
chullybun Sep 22, 2026
3b831b7
Fix compiler warnings introduced by TesterBaseCore extraction
chullybun Sep 22, 2026
cca96ae
Add UnitTestEx.Aspire: multi-host testing via AspireTester (Phase 2)
chullybun Sep 23, 2026
63c0ce2
Fix Aspire test race: add HTTP health check so WaitForResourceAsync i…
chullybun Sep 23, 2026
1ef9847
Fix Aspire CI failure: declare endpoints explicitly instead of relyin…
chullybun Sep 23, 2026
b1d1d39
Fix Aspire CI hang: bypass TLS cert validation on the test AppHost + …
chullybun Sep 23, 2026
a2b6e21
Default WaitForResourceAsync to a 60s fail-fast timeout instead of wa…
chullybun Sep 23, 2026
1f8107d
Fix CS1734 warning: remove invalid paramref from DefaultWaitForResour…
chullybun Sep 23, 2026
5ba642c
Fix VS build failure (MSB4057 GetTargetPath) for AppHost's UnitTestEx…
chullybun Sep 23, 2026
728e08f
Quiet Aspire's own AppHost/DCP bootstrap chatter and resource console…
chullybun Sep 23, 2026
e2e211a
Surface Aspire resource logging into the existing tester LOGGING > se…
chullybun Sep 23, 2026
453722c
Reformat Aspire resource log entries to the standard UnitTestEx Logge…
chullybun Sep 23, 2026
c06a656
Add Reason() and WaitAndLog() to TesterBaseCore for both Tier 1 and T…
chullybun Sep 23, 2026
128f74d
Remove dead AspireTesterBase.MinimumLogLevel property
chullybun Sep 23, 2026
7fa1868
Add Reason()/WaitAndLog() test coverage across MSTest, Xunit and NUni…
chullybun Sep 23, 2026
56be73b
Rename WaitAndLog to Wait, dedupe Aspire resource logs, suppress AppH…
chullybun Sep 23, 2026
112b279
Add HttpMock (WireMock.Net) fluent API for Aspire multi-host testing
chullybun Sep 23, 2026
187dfae
Add shared IHttpMock* interfaces for cross-tier HttpMock stubbing
chullybun Sep 24, 2026
09e62e8
Document Aspire multi-host testing in README
chullybun Sep 24, 2026
133b9a1
Remove design doc; add Aspire guidance quote to README
chullybun Sep 24, 2026
84db75f
Add UnitTestEx.Aspire to README Status table
chullybun Sep 24, 2026
cef7aa5
Add shared IHttpMockClient.WithRequestsFromResourceAsync to close YAM…
chullybun Sep 24, 2026
5ab5079
Add UnitTestEx.Aspire for .NET Aspire testing in v5.12.0
chullybun Sep 24, 2026
9fd9185
Add self-hosted WireMock.Net MockHost pattern, JsonElementComparerMat…
chullybun Sep 24, 2026
19ea683
Fix ApiTesterBase.CreateHttpClient silently resetting SharedState per…
chullybun Sep 24, 2026
59b1bae
refactor(aspire): split AspireTesterBase into non-generic base + gene…
chullybun Sep 26, 2026
5d62a61
Rename Reason to Checkpoint; add WaitForResourceAsync(string[]); move…
chullybun Sep 27, 2026
321a85e
Update CHANGELOG for Checkpoint/Delay/WaitForResourceAsync; dedupe Wa…
chullybun Sep 27, 2026
9381139
Add AspireTesterBase.BeforeStart hook to run setup before resources s…
chullybun Sep 28, 2026
6b13583
Add AspireTesterBase.GetConnectionStringAsync for use within BeforeStart
chullybun Sep 28, 2026
df8ee80
Add AfterStart hook, seal OnHostStartUp, move WaitForResourceAsync to…
chullybun Sep 28, 2026
9415461
Add DistributedApplication.HttpMock extension for tester-free stub se…
chullybun Sep 28, 2026
0ce1cb7
Add ErrorWhenLogContains (Aspire) and ExpectLogNotContains
chullybun Sep 29, 2026
ed7662e
Fix WriteDelay silently discarding pre-existing log backlog
chullybun Sep 29, 2026
3da3350
Fix racy watermark/snapshot split in AspireTesterBase log draining
chullybun Sep 29, 2026
0428196
Remove accidentally committed scratch file
chullybun Sep 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,13 @@ jobs:
dotnet test UnitTestEx.Xunit.Test.dll --no-build --verbosity normal --tests ProductFunctionTest
dotnet test UnitTestEx.Xunit.Test.dll --no-build --verbosity normal --tests ServiceBusFunctionTest
dotnet test UnitTestEx.Xunit.Test.dll --no-build --verbosity normal --tests PersonControllerTest
dotnet test UnitTestEx.Xunit.Test.dll --no-build --verbosity normal --tests ProductControllerTest
dotnet test UnitTestEx.Xunit.Test.dll --no-build --verbosity normal --tests ProductControllerTest

- name: Explicit Aspire MSTest test
run: dotnet test tests/UnitTestEx.Aspire.MSTest.Test/UnitTestEx.Aspire.MSTest.Test.csproj --no-build --verbosity normal

- name: Explicit Aspire NUnit test
run: dotnet test tests/UnitTestEx.Aspire.NUnit.Test/UnitTestEx.Aspire.NUnit.Test.csproj --no-build --verbosity normal

- name: Explicit Aspire Xunit test
run: dotnet test tests/UnitTestEx.Aspire.Xunit.Test/UnitTestEx.Aspire.Xunit.Test.csproj --no-build --verbosity normal
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,21 @@

Represents the **NuGet** versions.

## v5.12.0
- *Enhancement:* Added `UnitTestEx.Aspire` (NET8+) to enable testing of [.NET Aspire](https://learn.microsoft.com/en-us/dotnet/aspire/) multi-host (inter-domain) testing alongside existing in-process `WebApplicationFactory`. The API surface between the two is consistent where appropriate.
- `HttpClient` mocking of external dependencies is supported via `AspireHttpMockClient`, which stubs a real, out-of-process [WireMock.Net](https://wiremock.org/dotnet/) server resource - see the README's [Aspire multi-host testing](./README.md#Aspire-multi-host-testing) section for the recommended, self-hosted (Docker-free) pattern.
- `AspireTesterBase<TAppHost, TSelf>.BeforeStart(Func<DistributedApplication, Task>)` registers a callback that runs after the `DistributedApplication` is built, but before any resource is started; this enables use cases such as running database migrations/seeding, or clearing a cache, against a pre-existing (e.g. `AddConnectionString`) resource before dependent project resources come online. `AspireTesterBase.GetConnectionStringAsync(DistributedApplication, string, CancellationToken)` resolves such a resource's connection string directly (Aspire's own `DistributedApplication.GetConnectionStringAsync` testing extension throws until the application has started, so cannot be used from within a `BeforeStart` callback).
- `AspireTesterBase<TAppHost, TSelf>.AfterStart(Func<DistributedApplication, Task>)` registers a callback that runs once all resources have started (immediately after `DistributedApplication.StartAsync()` returns) - the natural place for `Aspire.Hosting.WaitForResourceAsync(this DistributedApplication, ...)` calls, since resources are guaranteed to exist by this point. As with `BeforeStart`, a throwing callback aborts start-up and propagates the exception. `AspireTesterBase.OnHostStartUp()` is `sealed` - it is a single-host-oriented hook that does not apply to a multi-host `DistributedApplication`; use `BeforeStart`/`AfterStart` instead.
- `AspireTesterBase.WaitForResourceAsync(string, TimeSpan?)`/`WaitForResourceAsync(string[], TimeSpan?)` delegate to `Aspire.Hosting.WaitForResourceAsync(this DistributedApplication, ...)` extension methods, discoverable alongside `AddProject`/`WithEnvironment` etc. within an AppHost project.
- `Aspire.Hosting.HttpMock(this DistributedApplication, string, string?, JsonElementComparerOptions?)` exposes the same fluent `AspireHttpMockClient` stubbing API directly against a started `DistributedApplication`, with no `AspireTester` required - enabling an `AppHost.cs` itself to pre-seed default request/response stubs so an exploratory/manual run (outside of any test) does not fail against an un-stubbed external dependency; see the README's [Aspire multi-host testing](./README.md#Aspire-multi-host-testing) "Pre-seeding stubs from `AppHost.cs`" subsection.
- `AspireTesterBase<TAppHost, TSelf>.ErrorWhenLogContains(LogLevel, string[]?, string[]?)` registers a continuous, streaming check that immediately fails the test the moment any resource logs an entry at or above the specified `LogLevel` (default `Error`) - checked every time resource log activity is drained, i.e. by every `Checkpoint`/`Delay`/`Http` call, not a single end-of-test scan. This is **enabled by default** at `LogLevel.Error` without ever needing to call it - pass `LogLevel.None` to opt out entirely for a test that deliberately induces a resource-level error/failure log as its subject. Accepts `*`/`?` wildcard `exclude` (known/expected noise to suppress; always wins) and `include` (narrows rather than widens what is checked) patterns, matched case-insensitively; each call fully replaces the prior configuration rather than merging with it. Since only a *drained* log entry is checked, a trailing `Test.Checkpoint("Final log check.")` is recommended at the end of a test relying on this to also catch background/hosted-service-only activity that no other call happens to drain.
- `AspireTesterBase<TAppHost, TSelf>.AssertLogContains(string, string?)`/`AssertLogNotContains(string, string?)` provide an immediate, one-shot positive/negative assertion (a Tier 2/3 equivalent of Tier 1's `ExpectLogContains`/`ExpectLogNotContains`) against **every** resource log entry captured for the lifetime of the test so far - across all resources, or scoped to one via the optional resource name - regardless of whether it has already been *drained* (unlike `ErrorWhenLogContains`'s `exclude`/`include`, which only ever narrow what counts as a *violation* of its own streaming check and never confirm any text was actually seen). Matched using the same `*`/`?` wildcard, case-insensitive "contains" semantics.
- `AspireTesterBase<TAppHost, TSelf>.ResetLogs()` discards every resource log entry captured so far, across all resources, along with the drain watermarks used by `Checkpoint`/`Delay` - relevant only where an `AspireTester`/`DistributedApplication` host is deliberately reused across multiple tests (e.g. via a shared fixture), so a later test's `ErrorWhenLogContains`/`AssertLogContains`/`AssertLogNotContains` checks are not tripped up (or falsely satisfied) by an earlier test's log activity. Deliberately explicit rather than automatic, since Tier 2/3 has no single per-test Act boundary to hang an automatic reset off, and background/hosted-service activity occurring between calls is exactly what this capture exists to surface.
- `WireMockConsole.RunAsync` builds an `AspireWireMockServerSettings` - a plain `WireMockServerSettings` subclass (`WireMock.Net` deliberately leaves it unsealed) adding UnitTestEx-specific configuration as a single, discoverable extension point rather than an ever-growing `RunAsync` parameter list - and registers a `WireMockRequestResponseLogger` against it that logs every genuine stubbed request/response pair (not the admin API calls used to configure mappings) through the self-hosted resource's own console output - captured/drained the same way as any other resource log entry - giving parity with Tier 1's `MockHttpClientHandler` request/response logging; defaults to `LogLevel.Information` (configurable via `AspireWireMockServerSettings.RequestResponseLogLevel`, read live so it may be set from within the caller-supplied server factory itself, or `LogLevel.None` to disable).
- *Enhancement:* Extended `TesterBase`/`AspireTesterBase.Delay` to accept an optional `reason` and to write any log messages captured since the last `Checkpoint()`/`Delay()` call to the test output (staying silent when there is nothing new to report). Added a new `Checkpoint(string reason)` method that provides the same reporting/log-flushing behavior without pausing.
- *Enhancement:* Added `ExpectLogNotContains` expectation (mirroring the existing `ExpectLogContains`) to confirm that the log output does **not** contain the specified text.
- *Enhancement:* `MockHttpClientFactory`/`MockHttpClientHandler`'s request/response logging level is now configurable via the new `MockHttpClientFactory.LogLevel` property (or fluent `UseLogLevel(LogLevel)`); defaults to `LogLevel.Debug`, unchanged from prior behavior. Set to `LogLevel.None` to disable this logging entirely.

## v5.11.1
- *Fixed:* Wrapped `Assembly.LoadFrom` in `TestSetUp` with try-catch to log exceptions and prevent setup failures.

Expand Down
2 changes: 1 addition & 1 deletion Common.targets
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>5.11.1</Version>
<Version>5.12.0</Version>
<LangVersion>preview</LangVersion>
<Authors>Avanade</Authors>
<Company>Avanade</Company>
Expand Down
Loading
Loading