aspire-integration-testing by aaronontheweb/dotnet-skills
npx skills add https://github.com/aaronontheweb/dotnet-skills --skill aspire-integration-testing在以下情况下使用此技能:
127.0.0.1:0)以避免冲突IAsyncLifetime 进行正确的测试夹具设置和清理广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
┌─────────────────┐ ┌──────────────────────┐
│ xUnit test file │──uses────────────►│ AspireFixture │
└─────────────────┘ │ (IAsyncLifetime) │
└──────────────────────┘
│
│ starts
▼
┌───────────────────────────┐
│ DistributedApplication │
│ (from AppHost) │
└───────────────────────────┘
│ exposes
▼
┌──────────────────────────────┐
│ Dynamic HTTP Endpoints │
└──────────────────────────────┘
│ consumed by
▼
┌─────────────────────────┐
│ HttpClient / Playwright│
└─────────────────────────┘
<ItemGroup>
<PackageReference Include="Aspire.Hosting.Testing" Version="$(AspireVersion)" />
<PackageReference Include="xunit" Version="*" />
<PackageReference Include="xunit.runner.visualstudio" Version="*" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="*" />
</ItemGroup>
当运行许多每个都启动一个 IHost 的集成测试时,默认的 .NET 主机构建器会为配置重新加载启用文件监视器。这会在 Linux 上耗尽文件描述符限制。
在任何测试运行之前,将此添加到你的测试项目中:
// TestEnvironmentInitializer.cs
using System.Runtime.CompilerServices;
namespace YourApp.Tests;
internal static class TestEnvironmentInitializer
{
[ModuleInitializer]
internal static void Initialize()
{
// 在测试主机中禁用配置文件监视
// 防止在 Linux 上耗尽文件描述符(inotify 监视限制)
Environment.SetEnvironmentVariable("DOTNET_HOSTBUILDER__RELOADCONFIGONCHANGE", "false");
}
}
using Aspire.Hosting;
using Aspire.Hosting.Testing;
public sealed class AspireAppFixture : IAsyncLifetime
{
private DistributedApplication? _app;
public DistributedApplication App => _app
?? throw new InvalidOperationException("App not initialized");
public async Task InitializeAsync()
{
var builder = await DistributedApplicationTestingBuilder
.CreateAsync<Projects.YourApp_AppHost>([
"YourApp:UseVolumes=false",
"YourApp:Environment=IntegrationTest",
"YourApp:Replicas=1"
]);
_app = await builder.BuildAsync();
using var startupCts = new CancellationTokenSource(TimeSpan.FromMinutes(10));
await _app.StartAsync(startupCts.Token);
using var healthCts = new CancellationTokenSource(TimeSpan.FromMinutes(5));
await _app.ResourceNotifications.WaitForResourceHealthyAsync("api", healthCts.Token);
}
public Uri GetEndpoint(string resourceName, string scheme = "https")
{
return _app?.GetEndpoint(resourceName, scheme)
?? throw new InvalidOperationException($"Endpoint for '{resourceName}' not found");
}
public async Task DisposeAsync()
{
if (_app is not null)
{
await _app.DisposeAsync();
}
}
}
[CollectionDefinition("Aspire collection")]
public class AspireCollection : ICollectionFixture<AspireAppFixture> { }
[Collection("Aspire collection")]
public class IntegrationTests
{
private readonly AspireAppFixture _fixture;
public IntegrationTests(AspireAppFixture fixture)
{
_fixture = fixture;
}
[Fact]
public async Task Application_ShouldStart()
{
var httpClient = _fixture.App.CreateHttpClient("yourapp");
var response = await httpClient.GetAsync("/");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
}
有关端点发现、数据库测试、Playwright UI 测试、条件资源配置、Respawn 数据库重置、服务间通信和消息队列测试模式,请参阅 advanced-patterns.md。
| 模式 | 使用场景 |
|---|---|
| 基本夹具 | 简单的 HTTP 端点测试 |
| 端点发现 | 避免硬编码 URL |
| 数据库测试 | 验证数据访问层 |
| Playwright 集成 | 使用真实后端进行完整的 UI 测试 |
| 配置覆盖 | 特定于测试的设置 |
| 健康检查 | 确保服务就绪 |
| 服务通信 | 测试分布式系统交互 |
| 消息队列测试 | 验证异步消息传递 |
| 问题 | 解决方案 |
|---|---|
| 测试立即超时 | 调用 await _app.StartAsync() 并等待服务健康 |
| 测试间端口冲突 | 使用 xUnit CollectionDefinition 共享夹具 |
| 由于时序导致的测试不稳定 | 实现正确的健康检查轮询,而不是使用 Task.Delay() |
| 无法连接到 SQL Server | 通过 GetConnectionStringAsync() 动态检索连接字符串 |
| 并行测试相互干扰 | 使用 [Collection] 属性顺序运行相关测试 |
| Aspire 仪表板冲突 | 一次只能运行一个仪表板;测试重用同一实例 |
IAsyncLifetime - 确保正确的异步初始化和清理DisposeAsync有关 GitHub Actions 设置、自定义资源等待器和 Aspire CLI/MCP 集成,请参阅 ci-and-tooling.md。
http://localhost:15888 检查仪表板ASPIRE_ALLOW_UNSECURED_TRANSPORT=true 以获得更详细的输出docker logs 检查容器输出每周安装次数
74
仓库
GitHub 星标数
488
首次出现
2026 年 1 月 29 日
安全审计
安装于
claude-code57
codex47
opencode46
github-copilot45
gemini-cli44
cursor42
Use this skill when:
127.0.0.1:0) to avoid conflictsIAsyncLifetime for proper test fixture setup and teardown┌─────────────────┐ ┌──────────────────────┐
│ xUnit test file │──uses────────────►│ AspireFixture │
└─────────────────┘ │ (IAsyncLifetime) │
└──────────────────────┘
│
│ starts
▼
┌───────────────────────────┐
│ DistributedApplication │
│ (from AppHost) │
└───────────────────────────┘
│ exposes
▼
┌──────────────────────────────┐
│ Dynamic HTTP Endpoints │
└──────────────────────────────┘
│ consumed by
▼
┌─────────────────────────┐
│ HttpClient / Playwright│
└─────────────────────────┘
<ItemGroup>
<PackageReference Include="Aspire.Hosting.Testing" Version="$(AspireVersion)" />
<PackageReference Include="xunit" Version="*" />
<PackageReference Include="xunit.runner.visualstudio" Version="*" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="*" />
</ItemGroup>
When running many integration tests that each start an IHost, the default .NET host builder enables file watchers for configuration reload. This exhausts file descriptor limits on Linux.
Add this to your test project before any tests run:
// TestEnvironmentInitializer.cs
using System.Runtime.CompilerServices;
namespace YourApp.Tests;
internal static class TestEnvironmentInitializer
{
[ModuleInitializer]
internal static void Initialize()
{
// Disable config file watching in test hosts
// Prevents file descriptor exhaustion (inotify watch limit) on Linux
Environment.SetEnvironmentVariable("DOTNET_HOSTBUILDER__RELOADCONFIGONCHANGE", "false");
}
}
using Aspire.Hosting;
using Aspire.Hosting.Testing;
public sealed class AspireAppFixture : IAsyncLifetime
{
private DistributedApplication? _app;
public DistributedApplication App => _app
?? throw new InvalidOperationException("App not initialized");
public async Task InitializeAsync()
{
var builder = await DistributedApplicationTestingBuilder
.CreateAsync<Projects.YourApp_AppHost>([
"YourApp:UseVolumes=false",
"YourApp:Environment=IntegrationTest",
"YourApp:Replicas=1"
]);
_app = await builder.BuildAsync();
using var startupCts = new CancellationTokenSource(TimeSpan.FromMinutes(10));
await _app.StartAsync(startupCts.Token);
using var healthCts = new CancellationTokenSource(TimeSpan.FromMinutes(5));
await _app.ResourceNotifications.WaitForResourceHealthyAsync("api", healthCts.Token);
}
public Uri GetEndpoint(string resourceName, string scheme = "https")
{
return _app?.GetEndpoint(resourceName, scheme)
?? throw new InvalidOperationException($"Endpoint for '{resourceName}' not found");
}
public async Task DisposeAsync()
{
if (_app is not null)
{
await _app.DisposeAsync();
}
}
}
[CollectionDefinition("Aspire collection")]
public class AspireCollection : ICollectionFixture<AspireAppFixture> { }
[Collection("Aspire collection")]
public class IntegrationTests
{
private readonly AspireAppFixture _fixture;
public IntegrationTests(AspireAppFixture fixture)
{
_fixture = fixture;
}
[Fact]
public async Task Application_ShouldStart()
{
var httpClient = _fixture.App.CreateHttpClient("yourapp");
var response = await httpClient.GetAsync("/");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
}
See advanced-patterns.md for Endpoint Discovery, Database Testing, Playwright UI Tests, Conditional Resource Configuration, Respawn database reset, Service-to-Service Communication, and Message Queue testing patterns.
| Pattern | Use Case |
|---|---|
| Basic Fixture | Simple HTTP endpoint testing |
| Endpoint Discovery | Avoid hard-coded URLs |
| Database Testing | Verify data access layer |
| Playwright Integration | Full UI testing with real backend |
| Configuration Override | Test-specific settings |
| Health Checks | Ensure services are ready |
| Service Communication | Test distributed system interactions |
| Message Queue Testing | Verify async messaging |
| Problem | Solution |
|---|---|
| Tests timeout immediately | Call await _app.StartAsync() and wait for services to be healthy |
| Port conflicts between tests | Use xUnit CollectionDefinition to share fixtures |
| Flaky tests due to timing | Implement proper health check polling instead of Task.Delay() |
| Can't connect to SQL Server | Retrieve connection string dynamically via GetConnectionStringAsync() |
| Parallel tests interfere | Use [Collection] attribute to run related tests sequentially |
| Aspire dashboard conflicts | Only one dashboard can run at a time; tests reuse the same instance |
IAsyncLifetime - Ensures proper async initialization and cleanupDisposeAsync properlySee ci-and-tooling.md for GitHub Actions setup, custom resource waiters, and Aspire CLI/MCP integration.
http://localhost:15888ASPIRE_ALLOW_UNSECURED_TRANSPORT=true for more verbose outputdocker logs to inspect container outputWeekly Installs
74
Repository
GitHub Stars
488
First Seen
Jan 29, 2026
Security Audits
Gen Agent Trust HubPassSocketFailSnykPass
Installed on
claude-code57
codex47
opencode46
github-copilot45
gemini-cli44
cursor42
测试策略完整指南:单元/集成/E2E测试金字塔与自动化实践
11,200 周安装