Lesson 3.8.1 had a paragraph on Aspire tests in outline, with this sentence in it: such tests are slow, because they start everything, and they check a lot at once; use them for a few paths from end to end. This lesson builds exactly one such path: a visitor books an appointment, the Kladde stores the booking, and the outbox sends the confirmation. In between lie the web app, SQL Server, the migrations, the outbox’s background service and the configuration the AppHost hands to all of them. No other test in the Kladde checks how all these parts work together with the wiring they use on your machine too.

The whole AppHost in a test

The package Aspire.Hosting.Testing starts an AppHost in a test. The test gets the same model that aspire run starts, with three differences: the dashboard is off, the ports are chosen at random so that several runs can go side by side, and the test can change the model before it starts.

C#
IDistributedApplicationTestingBuilder builder =
    await DistributedApplicationTestingBuilder.CreateAsync<Projects.Kladde_AppHost>(args, cancellationToken);
// ... change the model ...
DistributedApplication app = await builder.BuildAsync(cancellationToken);
await app.StartAsync(cancellationToken);
await app.ResourceNotifications.WaitForResourceHealthyAsync("web", cancellationToken);
  • CreateAsync<Projects.Kladde_AppHost>(args, …) runs AppHost.cs up to builder.Build() and stops the model before anything starts. Projects.Kladde_AppHost exists as soon as the test project references the AppHost. args go into the AppHost’s configuration, as with aspire run -- ….
  • BuildAsync and StartAsync build and start the model. StartAsync returns when everything has started, not when everything is ready.
  • ResourceNotifications.WaitForResourceHealthyAsync("web", …) waits until the health check from lesson 3.14.1 succeeds. Because web starts only after the migrations and /health/ready checks the database, the test thereby waits for everything a booking needs.
  • app.CreateHttpClient("web", "https") gives you an HttpClient whose base address is the https endpoint of web, with this run’s random port.
  • app.ResourceCommands.ExecuteCommandAsync("web", "test-data", …) runs a command the way the dashboard does, including the one from lesson 3.14.1. Its result is in Success, Message and, with ResultMode = HttpCommandResultMode.Text, in Data.Value.

An Aspire test starts processes and containers, not classes in the test process. So you can’t replace a service inside the web app’s container the way WebApplicationFactory allows. Whatever a test wants to change, it changes through the model: arguments, environment variables, resources.