Add E2E.Tests Aspire xUnit project#49
Conversation
Closes #48 - Add tests/E2E.Tests/E2E.Tests.csproj (net10.0, Aspire xUnit) - Add tests/E2E.Tests/GlobalUsings.cs with standard header - Add tests/E2E.Tests/xunit.runner.json (parallelizeAssembly=false) - Add tests/E2E.Tests/WebAppTests.cs (smoke test: GET / -> 200 OK) - Update MyBlog.slnx to include E2E.Tests under /tests/ folder Working as Pippin (test specialist) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
SummarySummary
CoverageAppHost - 0%
Domain - 93.4%
ServiceDefaults - 0%
Web - 68.1%
|
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds a new end-to-end (E2E) xUnit test project that boots the full Aspire distributed application and validates basic HTTP behavior, and wires it into the solution.
Changes:
- Added
tests/E2E.TestsxUnit project referencing the AppHost and configuring xUnit runner settings. - Implemented an initial smoke test that starts the Aspire stack and asserts
GET /returns200 OK. - Updated the solution (
MyBlog.slnx) to include the new E2E test project.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/E2E.Tests/E2E.Tests.csproj | New E2E test project targeting a new TFM with test/Aspire dependencies and AppHost reference |
| tests/E2E.Tests/GlobalUsings.cs | Global usings for Aspire testing, xUnit, and FluentAssertions |
| tests/E2E.Tests/xunit.runner.json | xUnit runner configuration to control parallelization and display options |
| tests/E2E.Tests/WebAppTests.cs | Smoke test that boots the distributed app and checks / returns 200 |
| MyBlog.slnx | Adds E2E.Tests project under the /tests/ solution folder |
| "methodDisplay": "method", | ||
| "methodDisplayOptions": "all", | ||
| "parallelizeAssembly": false, | ||
| "parallelizeTestCollections": true |
There was a problem hiding this comment.
parallelizeTestCollections is effectively moot when parallelizeAssembly is false, which can be confusing for future test additions. Consider setting both to false for clarity, or enable assembly parallelization if you truly want collections to run concurrently.
| "parallelizeTestCollections": true | |
| "parallelizeTestCollections": false |
|
|
||
| public class WebAppTests | ||
| { | ||
| private static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(60); |
There was a problem hiding this comment.
Timeout handling is currently duplicated: a 60s CancellationTokenSource governs the entire test, and then each step also adds a separate WaitAsync(DefaultTimeout, cancellationToken). This makes it harder to reason about total time budget and failure modes (cancellation vs timeout exceptions). Consider either (a) using the CTS token only (no WaitAsync(timeout)), or (b) using step-specific timeouts without a global 60s CTS (or a larger global CTS plus smaller per-step timeouts).
| public async Task GetWebResourceRootReturnsOkStatusCode() | ||
| { | ||
| // Arrange | ||
| var cancellationToken = new CancellationTokenSource(DefaultTimeout).Token; |
There was a problem hiding this comment.
Timeout handling is currently duplicated: a 60s CancellationTokenSource governs the entire test, and then each step also adds a separate WaitAsync(DefaultTimeout, cancellationToken). This makes it harder to reason about total time budget and failure modes (cancellation vs timeout exceptions). Consider either (a) using the CTS token only (no WaitAsync(timeout)), or (b) using step-specific timeouts without a global 60s CTS (or a larger global CTS plus smaller per-step timeouts).
| await using var app = await appHost.BuildAsync(cancellationToken).WaitAsync(DefaultTimeout, cancellationToken); | ||
| await app.StartAsync(cancellationToken).WaitAsync(DefaultTimeout, cancellationToken); | ||
|
|
||
| // Act | ||
| using var httpClient = app.CreateHttpClient("webfrontend"); | ||
| await app.ResourceNotifications | ||
| .WaitForResourceHealthyAsync("webfrontend", cancellationToken) | ||
| .WaitAsync(DefaultTimeout, cancellationToken); |
There was a problem hiding this comment.
Timeout handling is currently duplicated: a 60s CancellationTokenSource governs the entire test, and then each step also adds a separate WaitAsync(DefaultTimeout, cancellationToken). This makes it harder to reason about total time budget and failure modes (cancellation vs timeout exceptions). Consider either (a) using the CTS token only (no WaitAsync(timeout)), or (b) using step-specific timeouts without a global 60s CTS (or a larger global CTS plus smaller per-step timeouts).
| await using var app = await appHost.BuildAsync(cancellationToken).WaitAsync(DefaultTimeout, cancellationToken); | |
| await app.StartAsync(cancellationToken).WaitAsync(DefaultTimeout, cancellationToken); | |
| // Act | |
| using var httpClient = app.CreateHttpClient("webfrontend"); | |
| await app.ResourceNotifications | |
| .WaitForResourceHealthyAsync("webfrontend", cancellationToken) | |
| .WaitAsync(DefaultTimeout, cancellationToken); | |
| await using var app = await appHost.BuildAsync(cancellationToken); | |
| await app.StartAsync(cancellationToken); | |
| // Act | |
| using var httpClient = app.CreateHttpClient("webfrontend"); | |
| await app.ResourceNotifications | |
| .WaitForResourceHealthyAsync("webfrontend", cancellationToken); |
| await using var app = await appHost.BuildAsync(cancellationToken).WaitAsync(DefaultTimeout, cancellationToken); | ||
| await app.StartAsync(cancellationToken).WaitAsync(DefaultTimeout, cancellationToken); | ||
|
|
||
| // Act | ||
| using var httpClient = app.CreateHttpClient("webfrontend"); | ||
| await app.ResourceNotifications | ||
| .WaitForResourceHealthyAsync("webfrontend", cancellationToken) | ||
| .WaitAsync(DefaultTimeout, cancellationToken); |
There was a problem hiding this comment.
Timeout handling is currently duplicated: a 60s CancellationTokenSource governs the entire test, and then each step also adds a separate WaitAsync(DefaultTimeout, cancellationToken). This makes it harder to reason about total time budget and failure modes (cancellation vs timeout exceptions). Consider either (a) using the CTS token only (no WaitAsync(timeout)), or (b) using step-specific timeouts without a global 60s CTS (or a larger global CTS plus smaller per-step timeouts).
| await using var app = await appHost.BuildAsync(cancellationToken).WaitAsync(DefaultTimeout, cancellationToken); | |
| await app.StartAsync(cancellationToken).WaitAsync(DefaultTimeout, cancellationToken); | |
| // Act | |
| using var httpClient = app.CreateHttpClient("webfrontend"); | |
| await app.ResourceNotifications | |
| .WaitForResourceHealthyAsync("webfrontend", cancellationToken) | |
| .WaitAsync(DefaultTimeout, cancellationToken); | |
| await using var app = await appHost.BuildAsync(cancellationToken); | |
| await app.StartAsync(cancellationToken); | |
| // Act | |
| using var httpClient = app.CreateHttpClient("webfrontend"); | |
| await app.ResourceNotifications | |
| .WaitForResourceHealthyAsync("webfrontend", cancellationToken); |
| appHost.Services.ConfigureHttpClientDefaults(clientBuilder => | ||
| { | ||
| clientBuilder.AddStandardResilienceHandler(); | ||
| }); | ||
|
|
There was a problem hiding this comment.
AddStandardResilienceHandler() typically enables retries/backoff policies, which can make E2E failures slower and sometimes less deterministic (a failing endpoint may take longer to surface). For a smoke test, consider configuring a minimal/no-retry policy (or a test-specific resilience configuration) so failures are reported quickly and consistently.
| appHost.Services.ConfigureHttpClientDefaults(clientBuilder => | |
| { | |
| clientBuilder.AddStandardResilienceHandler(); | |
| }); |
Summary
Adds
tests/E2E.Tests— an Aspire xUnit project that spins up the full application viaDistributedApplicationTestingBuilderand validates HTTP-level behavior.Closes #48
Working as Pippin (test specialist)
Changes
tests/E2E.Tests/E2E.Tests.csprojnet10.0xUnit project referencing AppHosttests/E2E.Tests/GlobalUsings.cstests/E2E.Tests/xunit.runner.jsonparallelizeAssembly=falsetests/E2E.Tests/WebAppTests.csGET /→200 OKMyBlog.slnx/tests/folderTesting
DistributedApplicationTestingBuilder<Projects.MyBlog_AppHost>to start the full Aspire stackwebfrontendresource healthy (60s timeout)GET /returns200 OKProcess Note
squad/48-add-e2e-testsbranch.