用途
该技能面向 .NET 集成测试场景,帮助开发者在测试中使用真实基础设施而非模拟对象。适用情形包括:需要真实数据库、缓存或消息队列的集成测试;针对实际数据库测试数据访问层;验证消息队列集成;测试 Redis 缓存行为;避免对基础设施组件使用 Mock;确保测试运行在与生产相似的环境中;测试数据库迁移与架构变更。
主要能力
- 使用 Docker 容器提供真实数据库(SQL Server、PostgreSQL)、缓存(Redis)和消息队列(RabbitMQ)。
- 提供数据库模式示例(database-patterns.md):SQL Server、PostgreSQL 与迁移测试。
- 提供基础设施模式示例(infrastructure-patterns.md):Redis、RabbitMQ、多容器网络、容器复用与 Respawn。
- 覆盖 TestContainers 4.x 的模块构建器 API(3.0+ 模块构建器),并说明旧版类型(如 TestcontainersBuilder<T>、TestcontainersContainer、TestcontainersNetworkBuilder)已被重命名或移除,无法在现代包中编译。
- 给出所需 NuGet 包与命名空间引用。
- 提供常见问题(启动超时、端口占用、容器未清理、CI 与本地不一致)的解决方式。
- 提供 GitHub Actions 的 CI/CD 集成示例。
核心原则
- 真实基础设施优先于 Mock:使用容器中的真实数据库/服务,而非模拟。
- 测试隔离:每个测试获得全新容器或全新数据。
- 自动清理:TestContainers 负责容器生命周期与清理。
- 快速启动:在合适时于同一测试类中复用容器。
- 兼容 CI/CD:在支持 Docker 的 CI 环境中可无缝运行。
- 端口随机化:容器使用随机端口以避免冲突。
使用方式
推荐使用来自 Testcontainers.<Provider> 包的模块构建器,每个模块构建器会为其数据库或服务预配置正确的镜像、环境变量、端口和等待策略。将镜像传入构建器构造函数(而非通过 .WithImage()),以避免使用已过时的无参构造函数。示例:
var container = new PostgreSqlBuilder("postgres:latest")
.WithDatabase("testdb")
.WithUsername("postgres")
.WithPassword("postgres")
.Build();
所需 NuGet 包(模块包按提供方各一个):
<ItemGroup>
<!-- Core TestContainers -->
<PackageReference Include="Testcontainers" Version="*" />
<!-- Provider modules -->
<PackageReference Include="Testcontainers.MsSql" Version="*" />
<PackageReference Include="Testcontainers.PostgreSql" Version="*" />
<PackageReference Include="Testcontainers.Redis" Version="*" />
<PackageReference Include="Testcontainers.RabbitMq" Version="*" />
<PackageReference Include="xunit" Version="*" />
<PackageReference Include="xunit.runner.visualstudio" Version="*" />
<!-- Database drivers -->
<PackageReference Include="Microsoft.Data.SqlClient" Version="*" />
<PackageReference Include="Npgsql" Version="*" /> <!-- For PostgreSQL -->
<!-- Other infrastructure -->
<PackageReference Include="StackExchange.Redis" Version="*" /> <!-- For Redis -->
<PackageReference Include="RabbitMQ.Client" Version="*" /> <!-- For RabbitMQ -->
</ItemGroup>
所需 using:
using DotNet.Testcontainers.Builders;
using DotNet.Testcontainers.Networks;
using Testcontainers.MsSql;
using Testcontainers.PostgreSql;
using Testcontainers.Redis;
using Testcontainers.RabbitMq;
模块构建器与容器类型位于各提供方专属命名空间;基础类型(ContainerBuilder、NetworkBuilder、Wait)位于 DotNet.Testcontainers.Builders,INetwork 位于 DotNet.Testcontainers.Networks。
适用场景
- 数据访问层针对真实数据库的测试。
- 消息队列集成验证。
- Redis 缓存行为测试。
- 数据库迁移与架构变更测试。
- 需要生产相似环境的集成测试。
- 在支持 Docker 的 CI 中运行集成测试。
最佳实践
- 始终使用 IAsyncLifetime 进行正确的异步初始化与清理。
- 使用 Wait 策略等待端口可用,确保容器就绪。
- 使用随机端口,让 TestContainers 自动分配。
- 测试之间清理数据:使用全新容器或截断表。
- 尽可能复用容器,比每个测试新建更快。
- 测试真实查询,验证实际 SQL 行为。
- 验证约束:外键、唯一约束、索引。
- 测试事务:验证回滚与提交行为。
- 使用接近生产的数据量。
- 在 DisposeAsync 中始终释放容器。
常见问题与解决
- 容器启动超时:通过 WithWaitStrategy 设置等待策略与超时,例如:
_container = new PostgreSqlBuilder("postgres:latest")
.WithDatabase("testdb")
.WithUsername("postgres")
.WithPassword("postgres")
.WithWaitStrategy(Wait.ForUnixContainer()
.UntilInternalTcpPortIsAvailable(5432, w => w.WithTimeout(TimeSpan.FromMinutes(2))))
.Build();
- 端口已被占用:始终使用随机端口映射。模块构建器默认处理,核心容器构建器可显式设置:
.WithPortBinding(5432, true) // true = assign random public port
- 容器未清理:确保正确释放:
public async Task DisposeAsync()
{
await _connection?.DisposeAsync();
await _container?.DisposeAsync();
}
- 本地通过但 CI 失败:确保 CI 支持 Docker,例如 GitHub Actions 使用 runs-on: ubuntu-latest(预装 Docker)。
CI/CD 集成示例(GitHub Actions)
name: Integration Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 9.0.x
- name: Run Integration Tests
run: |
dotnet test tests/YourApp.IntegrationTests \
--filter Category=Integration \
--logger trx
- name: Cleanup Containers
if: always()
run: docker container prune -f
性能提示
- 复用容器:在集合中跨测试共享 fixture。
- 使用 Respawn:无需重建容器即可重置数据。
- 并行执行:TestContainers 自动处理端口冲突。
- 使用轻量镜像:Alpine 版本更小更快。
- 缓存镜像:Docker 会在本地缓存已拉取的镜像。
限制
资料未提供该技能的收费、价格、平台支持范围、效果指标或具体使用步骤之外的额外限制信息。