dotnet-testing-advanced-aspire-testing — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited dotnet-testing-advanced-aspire-testing (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
.NET Aspire Testing 是封閉式整合測試框架,專為分散式應用設計:
使用 .NET Aspire Testing 必須建立 AppHost 專案:
| 特性 | .NET Aspire Testing | Testcontainers |
|---|---|---|
| 設計目標 | 雲原生分散式應用 | 通用容器測試 |
| 配置方式 | AppHost 宣告式定義 | 程式碼手動配置 |
| 服務編排 | 自動處理 | 手動管理 |
| 學習曲線 | 較高 | 中等 |
| 適用場景 | 已用 Aspire 的專案 | 傳統 Web API |
MyApp/
├── src/
│ ├── MyApp.Api/ # WebApi 層
│ ├── MyApp.Application/ # 應用服務層
│ ├── MyApp.Domain/ # 領域模型
│ └── MyApp.Infrastructure/ # 基礎設施層
├── MyApp.AppHost/ # Aspire 編排專案 ⭐
│ ├── MyApp.AppHost.csproj
│ └── Program.cs
└── tests/
└── MyApp.Tests.Integration/ # Aspire Testing 整合測試
├── MyApp.Tests.Integration.csproj
├── Infrastructure/
│ ├── AspireAppFixture.cs
│ ├── IntegrationTestCollection.cs
│ ├── IntegrationTestBase.cs
│ └── DatabaseManager.cs
└── Controllers/
└── MyControllerTests.cs<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><Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Aspire.Hosting.Testing" Version="13.1.3" />
<PackageReference Include="AwesomeAssertions" Version="9.4.0" />
<PackageReference Include="AwesomeAssertions.Web" Version="1.9.6" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
<PackageReference Include="Respawn" Version="7.0.0" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\MyApp.AppHost\MyApp.AppHost.csproj" />
</ItemGroup>
</Project>使用 ContainerLifetime.Session 確保測試資源自動清理:
var postgres = builder.AddPostgres("postgres")
.WithLifetime(ContainerLifetime.Session);
var redis = builder.AddRedis("redis")
.WithLifetime(ContainerLifetime.Session);容器啟動與服務就緒是兩個階段,需要等待機制:
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 服務未能就緒");
}Aspire 啟動容器但不自動建立資料庫:
private async Task EnsureDatabaseExistsAsync(string connectionString)
{
var builder = new NpgsqlConnectionStringBuilder(connectionString);
var databaseName = builder.Database;
builder.Database = "postgres"; // 連到預設資料庫
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();
}
}使用 PostgreSQL 時指定適配器:
_respawner = await Respawner.CreateAsync(connection, new RespawnerOptions
{
TablesToIgnore = new Table[] { "__EFMigrationsHistory" },
SchemasToInclude = new[] { "public" },
DbAdapter = DbAdapter.Postgres // 明確指定(7.0 起可從 DbConnection 自動推斷,但建議保留)
});Respawn 7.0 升級提示: -DbAdapter現可從DbConnection型別自動推斷(使用NpgsqlConnection時自動選擇 Postgres),但明確指定仍建議以確保正確性 -Microsoft.Data.SqlClient不再作為傳遞依賴;若需重置 SQL Server 需自行加入該套件 - 新增 SQLite、IBM DB2、Snowflake 適配器支援 - 新增FormatDeleteStatement可自訂刪除語句
避免每個測試類別重複啟動容器:
[CollectionDefinition("Integration Tests")]
public class IntegrationTestCollection : ICollectionFixture<AspireAppFixture>
{
}
[Collection("Integration Tests")]
public class MyControllerTests : IntegrationTestBase
{
public MyControllerTests(AspireAppFixture fixture) : base(fixture) { }
}使用 TimeProvider 抽象化時間依賴:
// 服務實作
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 註冊
builder.Services.AddSingleton<TimeProvider>(TimeProvider.System);不要手動配置已由 Aspire 自動處理的端點:
// ❌ 錯誤:會造成衝突
builder.AddProject<Projects.MyApp_Api>("my-api")
.WithHttpEndpoint(port: 8080, name: "http");
// ✅ 正確:讓 Aspire 自動處理
builder.AddProject<Projects.MyApp_Api>("my-api")
.WithReference(postgresDb)
.WithReference(redis);PostgreSQL snake_case 與 C# PascalCase 的映射:
// 在 Program.cs 初始化
DapperTypeMapping.Initialize();
// 或使用 SQL 別名
const string sql = @"
SELECT id, name, price,
created_at AS CreatedAt,
updated_at AS UpdatedAt
FROM products";Program.cs,定義容器編排與服務參考AspireAppFixture.cs、IntegrationTestCollection.cs、IntegrationTestBase.csDatabaseManager.cs 處理資料庫初始化與 Respawn 清理*ControllerTests.cs),繼承 IntegrationTestBase.csproj,加入 Aspire.Hosting.Testing 與相關套件參考本技能內容提煉自「老派軟體工程師的測試修練 - 30 天挑戰」系列文章:
請參考同目錄下的範例檔案:
templates/apphost-program.cs - AppHost 編排定義templates/aspire-app-fixture.cs - 測試基礎設施templates/integration-test-collection.cs - Collection Fixture 設定templates/integration-test-base.cs - 測試基底類別templates/database-manager.cs - 資料庫管理員templates/controller-tests.cs - 控制器測試範例templates/test-project.csproj - 測試專案設定templates/apphost-project.csproj - AppHost 專案設定dotnet-testing-advanced-testcontainers-database - Testcontainers 資料庫測試dotnet-testing-advanced-testcontainers-nosql - Testcontainers NoSQL 測試dotnet-testing-advanced-webapi-integration-testing - 完整 WebAPI 整合測試~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.