mirror of
https://github.com/github/awesome-copilot.git
synced 2026-02-23 20:05:12 +00:00
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.
This commit is contained in:
281
skills/aspire/references/testing.md
Normal file
281
skills/aspire/references/testing.md
Normal file
@@ -0,0 +1,281 @@
|
||||
# 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
|
||||
|
||||
```xml
|
||||
<PackageReference Include="Aspire.Hosting.Testing" Version="*" />
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Core pattern: DistributedApplicationTestingBuilder
|
||||
|
||||
```csharp
|
||||
// 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
|
||||
|
||||
```csharp
|
||||
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
|
||||
|
||||
```csharp
|
||||
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
|
||||
|
||||
```csharp
|
||||
[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
|
||||
|
||||
```csharp
|
||||
[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
|
||||
|
||||
```csharp
|
||||
[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
|
||||
|
||||
```csharp
|
||||
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
|
||||
|
||||
```csharp
|
||||
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
|
||||
|
||||
```csharp
|
||||
var builder = await DistributedApplicationTestingBuilder
|
||||
.CreateAsync<Projects.AppHost>(args =>
|
||||
{
|
||||
args.Args = ["--environment", "Testing"];
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Connection string access
|
||||
|
||||
```csharp
|
||||
// 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:
|
||||
|
||||
```csharp
|
||||
[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
|
||||
```
|
||||
|
||||
```xml
|
||||
<!-- 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>
|
||||
```
|
||||
Reference in New Issue
Block a user