Skip to content

Add E2E.Tests Aspire xUnit project#49

Closed
mpaulosky wants to merge 1 commit into
devfrom
squad/48-add-e2e-tests
Closed

Add E2E.Tests Aspire xUnit project#49
mpaulosky wants to merge 1 commit into
devfrom
squad/48-add-e2e-tests

Conversation

@mpaulosky

Copy link
Copy Markdown
Owner

Summary

Adds tests/E2E.Tests — an Aspire xUnit project that spins up the full application via DistributedApplicationTestingBuilder and validates HTTP-level behavior.

Closes #48

Working as Pippin (test specialist)

Changes

File Action
tests/E2E.Tests/E2E.Tests.csproj Created — net10.0 xUnit project referencing AppHost
tests/E2E.Tests/GlobalUsings.cs Created — standard header + global usings
tests/E2E.Tests/xunit.runner.json Created — parallelizeAssembly=false
tests/E2E.Tests/WebAppTests.cs Created — smoke test: GET /200 OK
MyBlog.slnx Updated — E2E.Tests added under /tests/ folder

Testing

  • Smoke test uses DistributedApplicationTestingBuilder<Projects.MyBlog_AppHost> to start the full Aspire stack
  • Waits for webfrontend resource healthy (60s timeout)
  • Asserts GET / returns 200 OK
  • No Testcontainers needed — Aspire manages MongoDB + Redis infra

Process Note

⚠️ This task was flagged as "needs review" — initial implementation bypassed the plan workflow (no issue/branch/project tracking). Remediated: issue #48 created, added to Project #4, work moved to proper squad/48-add-e2e-tests branch.

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>
Copilot AI review requested due to automatic review settings April 19, 2026 19:43
@github-actions

Copy link
Copy Markdown
Contributor

Summary

Summary
Generated on: 04/19/2026 - 19:44:08
Coverage date: 04/19/2026 - 19:43:47 - 04/19/2026 - 19:43:55
Parser: MultiReport (4x Cobertura)
Assemblies: 4
Classes: 35
Files: 34
Line coverage: 64.3% (474 of 737)
Covered lines: 474
Uncovered lines: 263
Coverable lines: 737
Total lines: 2069
Branch coverage: 64% (169 of 264)
Covered branches: 169
Total branches: 264
Method coverage: Feature is only available for sponsors
Tag: 55_24637522862

Coverage

AppHost - 0%
Name Line Branch
AppHost 0% ****
Program 0%
Domain - 93.4%
Name Line Branch
Domain 93.4% 50%
Domain.Abstractions.Result 95% 50%
Domain.Abstractions.Result`1 75% 50%
MyBlog.Domain.Entities.BlogPost 100%
ServiceDefaults - 0%
Name Line Branch
ServiceDefaults 0% 0%
Microsoft.Extensions.Hosting.Extensions 0% 0%
Web - 68.1%
Name Line Branch
Web 68.1% 69%
MyBlog.Web.Components.Layout.MainLayout 100%
MyBlog.Web.Components.Layout.NavMenu 82.8% 71.4%
MyBlog.Web.Components.Pages.Error 100% 75%
MyBlog.Web.Components.Routes 0% 0%
MyBlog.Web.Components.Shared.RedirectToLogin 100%
MyBlog.Web.Data.BlogDbContext 100%
MyBlog.Web.Data.BlogPostDto 100%
MyBlog.Web.Data.BlogPostMappings 100%
MyBlog.Web.Data.MongoDbBlogPostRepository 100% 100%
MyBlog.Web.Features.BlogPosts.Create.Create 75% 50%
MyBlog.Web.Features.BlogPosts.Create.CreateBlogPostCommand 100%
MyBlog.Web.Features.BlogPosts.Create.CreateBlogPostHandler 100%
MyBlog.Web.Features.BlogPosts.Delete.ConfirmDeleteDialog 100% 100%
MyBlog.Web.Features.BlogPosts.Delete.DeleteBlogPostCommand 100%
MyBlog.Web.Features.BlogPosts.Delete.DeleteBlogPostHandler 100%
MyBlog.Web.Features.BlogPosts.Edit.Edit 88% 75%
MyBlog.Web.Features.BlogPosts.Edit.EditBlogPostCommand 100%
MyBlog.Web.Features.BlogPosts.Edit.EditBlogPostHandler 84% 90%
MyBlog.Web.Features.BlogPosts.Edit.GetBlogPostByIdQuery 100%
MyBlog.Web.Features.BlogPosts.List.GetBlogPostsHandler 100% 100%
MyBlog.Web.Features.BlogPosts.List.Index 86.8% 84.6%
MyBlog.Web.Features.UserManagement.AssignRoleCommand 100%
MyBlog.Web.Features.UserManagement.ManageRoles 90.4% 75%
MyBlog.Web.Features.UserManagement.Profile 97.2% 82%
MyBlog.Web.Features.UserManagement.RemoveRoleCommand 100%
MyBlog.Web.Features.UserManagement.RoleDto 100%
MyBlog.Web.Features.UserManagement.UserManagementHandler 0% 0%
MyBlog.Web.Features.UserManagement.UserWithRolesDto 100%
MyBlog.Web.Security.RoleClaimsHelper 91.8% 91.1%
Program 0% 0%

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.Tests xUnit project referencing the AppHost and configuring xUnit runner settings.
  • Implemented an initial smoke test that starts the Aspire stack and asserts GET / returns 200 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

Copilot AI Apr 19, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
"parallelizeTestCollections": true
"parallelizeTestCollections": false

Copilot uses AI. Check for mistakes.

public class WebAppTests
{
private static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(60);

Copilot AI Apr 19, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copilot uses AI. Check for mistakes.
public async Task GetWebResourceRootReturnsOkStatusCode()
{
// Arrange
var cancellationToken = new CancellationTokenSource(DefaultTimeout).Token;

Copilot AI Apr 19, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copilot uses AI. Check for mistakes.
Comment on lines +30 to +37
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);

Copilot AI Apr 19, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Suggested change
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);

Copilot uses AI. Check for mistakes.
Comment on lines +30 to +37
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);

Copilot AI Apr 19, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Suggested change
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);

Copilot uses AI. Check for mistakes.
Comment on lines +25 to +29
appHost.Services.ConfigureHttpClientDefaults(clientBuilder =>
{
clientBuilder.AddStandardResilienceHandler();
});

Copilot AI Apr 19, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
appHost.Services.ConfigureHttpClientDefaults(clientBuilder =>
{
clientBuilder.AddStandardResilienceHandler();
});

Copilot uses AI. Check for mistakes.
@mpaulosky mpaulosky closed this Apr 19, 2026
@mpaulosky
mpaulosky deleted the squad/48-add-e2e-tests branch April 19, 2026 20:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Sprint 3] Add E2E.Tests Aspire xUnit project

2 participants