Files
awesome-copilot/skills/aspire/references/testing.md
Chris McKee 6512c785c7 feat: Add Aspire polyglot distributed-app orchestration skill
Add comprehensive Aspire skill covering CLI, AppHost orchestration, service discovery, integrations (144+), MCP server, dashboard, testing, deployment, and troubleshooting. Includes reference docs for polyglot APIs, architecture, CLI, integrations catalog, and more.
2026-02-05 19:00:02 -06:00

7.4 KiB

Testing — Complete Reference

Aspire provides Aspire.Hosting.Testing for running integration tests against your full AppHost. Tests spin up the entire distributed application (or a subset) and run assertions against real services.


Package

<PackageReference Include="Aspire.Hosting.Testing" Version="*" />

Core pattern: DistributedApplicationTestingBuilder

// 1. Create a testing builder from your AppHost
var builder = await DistributedApplicationTestingBuilder
    .CreateAsync<Projects.MyAppHost>();

// 2. (Optional) Override resources for testing
// ... see customization section below

// 3. Build and start the application
await using var app = await builder.BuildAsync();
await app.StartAsync();

// 4. Create HTTP clients for your services
var client = app.CreateHttpClient("api");

// 5. Run assertions
var response = await client.GetAsync("/health");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);

xUnit examples

Basic health check test

public class HealthTests(ITestOutputHelper output)
{
    [Fact]
    public async Task AllServicesAreHealthy()
    {
        var builder = await DistributedApplicationTestingBuilder
            .CreateAsync<Projects.AppHost>();

        await using var app = await builder.BuildAsync();
        await app.StartAsync();

        // Test each service's health endpoint
        var apiClient = app.CreateHttpClient("api");
        var apiHealth = await apiClient.GetAsync("/health");
        Assert.Equal(HttpStatusCode.OK, apiHealth.StatusCode);

        var workerClient = app.CreateHttpClient("worker");
        var workerHealth = await workerClient.GetAsync("/health");
        Assert.Equal(HttpStatusCode.OK, workerHealth.StatusCode);
    }
}

API integration test

public class ApiTests(ITestOutputHelper output)
{
    [Fact]
    public async Task CreateOrder_ReturnsCreated()
    {
        var builder = await DistributedApplicationTestingBuilder
            .CreateAsync<Projects.AppHost>();

        await using var app = await builder.BuildAsync();
        await app.StartAsync();

        var client = app.CreateHttpClient("api");

        var order = new { ProductId = 1, Quantity = 2 };
        var response = await client.PostAsJsonAsync("/orders", order);

        Assert.Equal(HttpStatusCode.Created, response.StatusCode);

        var created = await response.Content.ReadFromJsonAsync<Order>();
        Assert.NotNull(created);
        Assert.Equal(1, created.ProductId);
    }
}

Testing with wait for readiness

[Fact]
public async Task DatabaseIsSeeded()
{
    var builder = await DistributedApplicationTestingBuilder
        .CreateAsync<Projects.AppHost>();

    await using var app = await builder.BuildAsync();
    await app.StartAsync();

    // Wait for the API to be fully ready (all dependencies healthy)
    await app.WaitForResourceReadyAsync("api");

    var client = app.CreateHttpClient("api");
    var response = await client.GetAsync("/products");

    Assert.Equal(HttpStatusCode.OK, response.StatusCode);
    var products = await response.Content.ReadFromJsonAsync<List<Product>>();
    Assert.NotEmpty(products);
}

MSTest examples

[TestClass]
public class IntegrationTests
{
    [TestMethod]
    public async Task ApiReturnsProducts()
    {
        var builder = await DistributedApplicationTestingBuilder
            .CreateAsync<Projects.AppHost>();

        await using var app = await builder.BuildAsync();
        await app.StartAsync();

        var client = app.CreateHttpClient("api");
        var response = await client.GetAsync("/products");

        Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
    }
}

NUnit examples

[TestFixture]
public class IntegrationTests
{
    [Test]
    public async Task ApiReturnsProducts()
    {
        var builder = await DistributedApplicationTestingBuilder
            .CreateAsync<Projects.AppHost>();

        await using var app = await builder.BuildAsync();
        await app.StartAsync();

        var client = app.CreateHttpClient("api");
        var response = await client.GetAsync("/products");

        Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
    }
}

Customizing the test AppHost

Override resources

var builder = await DistributedApplicationTestingBuilder
    .CreateAsync<Projects.AppHost>();

// Replace a real database with a test container
builder.Services.ConfigureHttpClientDefaults(http =>
{
    http.AddStandardResilienceHandler();
});

// Add test-specific configuration
builder.Configuration["TestMode"] = "true";

await using var app = await builder.BuildAsync();
await app.StartAsync();

Exclude resources

var builder = await DistributedApplicationTestingBuilder
    .CreateAsync<Projects.AppHost>(args =>
    {
        // Don't start the worker for API-only tests
        args.Args = ["--exclude-resource", "worker"];
    });

Test with specific environment

var builder = await DistributedApplicationTestingBuilder
    .CreateAsync<Projects.AppHost>(args =>
    {
        args.Args = ["--environment", "Testing"];
    });

Connection string access

// Get the connection string for a resource in tests
var connectionString = await app.GetConnectionStringAsync("db");

// Use it to query the database directly in tests
using var conn = new NpgsqlConnection(connectionString);
await conn.OpenAsync();
var count = await conn.ExecuteScalarAsync<int>("SELECT COUNT(*) FROM products");
Assert.True(count > 0);

Best practices

  1. Use WaitForResourceReadyAsync before making requests — ensures all dependencies are healthy
  2. Each test should be independent — don't rely on state from previous tests
  3. Use await using for the app — ensures cleanup even on test failure
  4. Test real infrastructure — Aspire spins up real containers (Redis, PostgreSQL, etc.), giving you high-fidelity integration tests
  5. Keep test AppHost lean — exclude resources you don't need for specific test scenarios
  6. Use test-specific configuration — override settings for test isolation
  7. Timeout protection — set reasonable test timeouts since containers take time to start:
[Fact(Timeout = 120_000)]  // 2 minutes
public async Task SlowIntegrationTest() { ... }

Project structure

MyApp/
├── src/
│   ├── MyApp.AppHost/           # AppHost project
│   ├── MyApp.Api/               # API service
│   ├── MyApp.Worker/            # Worker service
│   └── MyApp.ServiceDefaults/   # Shared defaults
└── tests/
    └── MyApp.Tests/             # Integration tests
        ├── MyApp.Tests.csproj   # References AppHost + Testing package
        └── ApiTests.cs          # Test classes
<!-- MyApp.Tests.csproj -->
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <IsAspireTestProject>true</IsAspireTestProject>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Aspire.Hosting.Testing" Version="*" />
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="*" />
    <PackageReference Include="xunit" Version="*" />
    <PackageReference Include="xunit.runner.visualstudio" Version="*" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\src\MyApp.AppHost\MyApp.AppHost.csproj" />
  </ItemGroup>
</Project>