本文へ移動
cccskills
無料GitHub で公開

dotnet-testing-advanced-aspire-testing

Complete guide for .NET Aspire Testing integration testing framework. Use when testing .NET Aspire distributed applications, configuring AppHost tests, or migrating from Testcontainers to Aspire testing. Covers DistributedApplicationTestingBuilder, container lifecycle management, multi-service orchestration, Respawn configuration, and time testability design. Keywords: aspire testing, .NET Aspire, DistributedApplicationTestingBuilder, AppHost testing, distributed testing, AspireAppFixture, IAsyncLifetime, ContainerLifetime.Session, cloud-native testing, multi-service integration, Aspire.Hosting.Testing, Respawn

インストール方法を見る

含まれるファイル(1)

  • SKILL.md10.9 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Source: kevintsengtw/dotnet-testing-agent-skills (MIT). Ported into dotnet-agent-harness.

.NET Aspire Testing Integration Testing Framework

Applicable Scenarios

Use this skill when asked to perform the following tasks:

  • Create integration tests for .NET Aspire distributed applications
  • Migrate from Testcontainers to .NET Aspire Testing
  • Configure AppHost projects for testing
  • Use DistributedApplicationTestingBuilder to establish testing environment
  • Test interactions between multiple services (database, cache, API, etc.)
  • Create integration testing architecture for cloud-native .NET applications

Prerequisites

  • .NET 8 SDK or higher
  • Docker Desktop (WSL 2 or Hyper-V)
  • AppHost project (.NET Aspire application orchestration)

Core Concepts

.NET Aspire Testing Positioning

.NET Aspire Testing is a closed integration testing framework designed specifically for distributed applications:

  • Reproduce the same service architecture in tests as in production
  • Use real containers instead of mock services
  • Automatic container lifecycle management

Necessity of AppHost Projects

Using .NET Aspire Testing requires creating an AppHost project:

  • Define complete application architecture and container orchestration
  • Tests reuse AppHost configuration to establish environment
  • Without AppHost, cannot use Aspire Testing

Differences from Testcontainers

Feature.NET Aspire TestingTestcontainers
Design GoalCloud-native distributed applicationsGeneral container testing
Configuration MethodAppHost declarative definitionManual code configuration
Service OrchestrationAutomatic handlingManual management
Learning CurveHigherMedium
Applicable ScenariosProjects already using AspireTraditional Web API

Project Structure

MyApp/
├── src/
│   ├── MyApp.Api/                    # Web API layer
│   ├── MyApp.Application/            # Application service layer
│   ├── MyApp.Domain/                 # Domain model
│   └── MyApp.Infrastructure/         # Infrastructure layer
├── MyApp.AppHost/                    # Aspire orchestration project ⭐
│   ├── MyApp.AppHost.csproj
│   └── Program.cs
└── tests/
    └── MyApp.Tests.Integration/      # Aspire Testing integration tests
        ├── MyApp.Tests.Integration.csproj
        ├── Infrastructure/
        │   ├── AspireAppFixture.cs
        │   ├── IntegrationTestCollection.cs
        │   ├── IntegrationTestBase.cs
        │   └── DatabaseManager.cs
        └── Controllers/
            └── MyControllerTests.cs
```text

## Required Packages

### AppHost Project

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <Sdk Name="Aspire.AppHost.Sdk" Version="9.0.0" />

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net9.0</TargetFramework>
    <IsAspireHost>true</IsAspireHost>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Aspire.Hosting.AppHost" Version="9.1.0" />
    <PackageReference Include="Aspire.Hosting.PostgreSQL" Version="9.1.0" />
    <PackageReference Include="Aspire.Hosting.Redis" Version="9.1.0" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\src\MyApp.Api\MyApp.Api.csproj" />
  </ItemGroup>
</Project>
```text

### Test Project

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
    <IsTestProject>true</IsTestProject>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Aspire.Hosting.Testing" Version="9.1.0" />
    <PackageReference Include="AwesomeAssertions" Version="9.1.0" />
    <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.0" />
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
    <PackageReference Include="Respawn" Version="6.2.1" />
    <PackageReference Include="xunit" Version="2.9.3" />
    <PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
  </ItemGroup>

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

## Container Lifecycle Management

Use `ContainerLifetime.Session` to ensure automatic cleanup of test resources:

```csharp
var postgres = builder.AddPostgres("postgres")
                     .WithLifetime(ContainerLifetime.Session);

var redis = builder.AddRedis("redis")
                  .WithLifetime(ContainerLifetime.Session);
```text

- **Session**: Automatically cleaned up after test session ends (recommended)
- **Persistent**: Containers continue running, require manual cleanup

## Waiting for Services Ready

Container startup and service readiness are two phases, requiring wait mechanism:

```csharp
private async Task WaitForPostgreSqlReadyAsync()
{
    const int maxRetries = 30;
    const int delayMs = 1000;

    for (int i = 0; i < maxRetries; i++)
    {
        try
        {
            var connectionString = await GetConnectionStringAsync();
            await using var connection = new NpgsqlConnection(connectionString);
            await connection.OpenAsync();
            return;
        }
        catch (Exception ex) when (i < maxRetries - 1)
        {
            await Task.Delay(delayMs);
        }
    }
    throw new InvalidOperationException("PostgreSQL service failed to become ready");
}
```text

## Database Initialization

Aspire starts containers but does not automatically create databases:

```csharp
private async Task EnsureDatabaseExistsAsync(string connectionString)
{
    var builder = new NpgsqlConnectionStringBuilder(connectionString);
    var databaseName = builder.Database;
    builder.Database = "postgres"; // Connect to default database

    await using var connection = new NpgsqlConnection(builder.ToString());
    await connection.OpenAsync();

    var checkDbQuery = $"SELECT 1 FROM pg_database WHERE datname = '{databaseName}'";
    var dbExists = await new NpgsqlCommand(checkDbQuery, connection).ExecuteScalarAsync();

    if (dbExists == null)
    {
        await new NpgsqlCommand($"CREATE DATABASE \"{databaseName}\"", connection)
            .ExecuteNonQueryAsync();
    }
}
```text

## Respawn Configuration

When using PostgreSQL, must specify adapter:

```csharp
_respawner = await Respawner.CreateAsync(connection, new RespawnerOptions
{
    TablesToIgnore = new Table[] { "__EFMigrationsHistory" },
    SchemasToInclude = new[] { "public" },
    DbAdapter = DbAdapter.Postgres  // Critical!
});
```text

## Collection Fixture Best Practices

Avoid restarting containers for each test class:

```csharp
[CollectionDefinition("Integration Tests")]
public class IntegrationTestCollection : ICollectionFixture<AspireAppFixture>
{
}

[Collection("Integration Tests")]
public class MyControllerTests : IntegrationTestBase
{
    public MyControllerTests(AspireAppFixture fixture) : base(fixture) { }
}
```text

## Time Testability

Use `TimeProvider` to abstract time dependencies:

```csharp
// Service implementation
public class ProductService
{
    private readonly TimeProvider _timeProvider;

    public ProductService(TimeProvider timeProvider)
    {
        _timeProvider = timeProvider;
    }

    public async Task<Product> CreateAsync(ProductCreateRequest request)
    {
        var now = _timeProvider.GetUtcNow();
        var product = new Product
        {
            CreatedAt = now,
            UpdatedAt = now
        };
        // ...
    }
}

// DI registration
builder.Services.AddSingleton<TimeProvider>(TimeProvider.System);
```text

## Selection Recommendations

### Choose .NET Aspire Testing

- Project already uses .NET Aspire
- Need to test multi-service interactions
- Emphasize unified development and testing experience
- Cloud-native application architecture

### Choose Testcontainers

- Traditional .NET project
- Need fine-grained container control
- Integration with non-.NET services
- Team unfamiliar with Aspire

## Common Issues

### Endpoint Configuration Conflicts

Don't manually configure endpoints already automatically handled by Aspire:

```csharp
// ❌ Error: causes conflicts
builder.AddProject<Projects.MyApp_Api>("my-api")
       .WithHttpEndpoint(port: 8080, name: "http");

// ✅ Correct: let Aspire handle automatically
builder.AddProject<Projects.MyApp_Api>("my-api")
       .WithReference(postgresDb)
       .WithReference(redis);
```text

### Dapper Field Mapping

PostgreSQL snake_case to C# PascalCase mapping:

```csharp
// Initialize in Program.cs
DapperTypeMapping.Initialize();

// Or use SQL aliases
const string sql = @"
    SELECT id, name, price,
           created_at AS CreatedAt,
           updated_at AS UpdatedAt
    FROM products";
```text

## Reference Resources

### Original Articles

This skill content is distilled from the "Old School Software Engineer's Testing Practice - 30 Day Challenge" article series:

- **Day 24 - .NET Aspire Testing Introduction**
  - Article: https://ithelp.ithome.com.tw/articles/10377071
  - Sample code: https://github.com/kevintsengtw/30Days_in_Testing_Samples/tree/main/day24

- **Day 25 - .NET Aspire Integration Testing: From Testcontainers to .NET Aspire Testing**
  - Article: https://ithelp.ithome.com.tw/articles/10377197
  - Sample code: https://github.com/kevintsengtw/30Days_in_Testing_Samples/tree/main/day25

### Official Documentation

- [.NET Aspire Official Documentation](https://learn.microsoft.com/dotnet/aspire/)
- [Aspire Testing Documentation](https://learn.microsoft.com/dotnet/aspire/testing)

## Reference Resources (continued)

Please refer to example files in the same directory:

- `templates/apphost-program.cs` - AppHost orchestration definition
- `templates/aspire-app-fixture.cs` - Testing infrastructure
- `templates/integration-test-collection.cs` - Collection Fixture configuration
- `templates/integration-test-base.cs` - Test base class
- `templates/database-manager.cs` - Database manager
- `templates/controller-tests.cs` - Controller test examples
- `templates/test-project.csproj` - Test project configuration
- `templates/apphost-project.csproj` - AppHost project configuration

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Patterns and techniques for evaluating and improving AI agent outputs.

日本語の概要は準備中です。原文の説明を表示しています。

rudironsoni/Synaxis22026年3月17日 更新

Comprehensive AI prompt engineering safety review and improvement prompt. Analyzes prompts for safety, bias, security vulnerabilities, and effectiveness while providing detailed improvement recommendations.

日本語の概要は準備中です。原文の説明を表示しています。

rudironsoni/Synaxis22026年3月17日 更新

Use when user requests research requiring multiple sources, comprehensive analysis, or synthesis across topics - technical research, domain knowledge gathering, market analysis, or learning about complex subjects

日本語の概要は準備中です。原文の説明を表示しています。

rudironsoni/Synaxis22026年3月17日 更新

deep-wiki

無料

AI-powered wiki generation for code repositories with commands, agents, and skills

日本語の概要は準備中です。原文の説明を表示しています。

rudironsoni/Synaxis22026年3月17日 更新

Use when building .NET 10 or C# 14 applications; when using minimal APIs, modular monolith patterns, or feature folders; when implementing HTTP resilience, Options pattern, Channels, or validation; when seeing outdated patterns like old extension method syntax

日本語の概要は準備中です。原文の説明を表示しています。

rudironsoni/Synaxis22026年3月17日 更新

Implements accessible .NET UI. SemanticProperties, ARIA, AutomationPeer, testing per platform.

日本語の概要は準備中です。原文の説明を表示しています。

rudironsoni/Synaxis22026年3月17日 更新

rudironsoni のスキルをすべて見る

このスキルの問題を報告する