mcpField
T
testcontainers

testcontainers

github.com/wshaddix测试与质量86566 次安装35 周前更新
代码仓库

关于 testcontainers

面向需要在集成测试中验证真实基础设施的开发者,介绍使用 Testcontainers 管理容器的测试实践。内容涵盖隔离、生命周期清理、随机端口及数据库和消息代理测试。

主要功能

  1. 01在容器中运行真实基础设施
  2. 02隔离测试数据与环境
  3. 03自动管理容器生命周期
  4. 04随机分配端口
  5. 05覆盖数据库和消息代理测试

适用场景

  1. 01针对真实数据库测试数据访问层
  2. 02验证消息队列集成
  3. 03测试数据库迁移和约束
  4. 04在 Docker CI 环境运行集成测试

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/wshaddix/dotnet-skills --skill testcontainers

安装后在 Claude Code 中输入/testcontainers确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

reference/
advanced-patterns.md
database-containers.md
message-broker-containers.md

详细介绍

使用 TestContainers 进行集成测试

何时使用此技能

适用于以下场景:

  • 编写需要真实基础设施(数据库、缓存、消息队列)的集成测试
  • 针对实际数据库测试数据访问层
  • 验证消息队列集成
  • 测试 Redis 缓存行为
  • 避免模拟基础设施组件
  • 确保测试在类生产环境中运行
  • 测试数据库迁移和架构变更

核心原则

  1. 使用真实基础设施,而非模拟对象——在容器中使用实际数据库和服务,而不是模拟对象
  2. 测试隔离——每项测试都使用全新的容器或全新的数据
  3. 自动清理——TestContainers 负责容器生命周期管理和清理
  4. 快速启动——适当时,在同一个类的测试之间复用容器
  5. 兼容 CI/CD——可在启用了 Docker 的 CI 环境中无缝运行
  6. 端口随机化——容器使用随机端口以避免冲突

为什么选择 TestContainers 而不是模拟对象?

模拟基础设施的问题
// BAD: Mocking a database
public class OrderRepositoryTests
{
    private readonly Mock<IDbConnection> _mockDb = new();

    [Fact]
    public async Task GetOrder_ReturnsOrder()
    {
        // This doesn't test real SQL behavior, constraints, or performance
        _mockDb.Setup(db => db.QueryAsync<Order>(It.IsAny<string>()))
            .ReturnsAsync(new[] { new Order { Id = 1 } });

        var repo = new OrderRepository(_mockDb.Object);
        var order = await repo.GetOrderAsync(1);

        Assert.NotNull(order);
    }
}

问题:

  • 无法测试实际的 SQL 查询
  • 会遗漏数据库约束、索引和性能问题
  • 可能造成虚假的信心
  • 无法发现 SQL 语法错误或架构不匹配
更好的选择:使用真实数据库的 TestContainers
// GOOD: Testing against a real database
public class OrderRepositoryTests : IAsyncLifetime
{
    private readonly TestcontainersContainer _dbContainer;
    private IDbConnection _connection;

    public OrderRepositoryTests()
    {
        _dbContainer = new TestcontainersBuilder<TestcontainersContainer>()
            .WithImage("mcr.microsoft.com/mssql/server:2022-latest")
            .WithEnvironment("ACCEPT_EULA", "Y")
            .WithEnvironment("SA_PASSWORD", "Your_password123")
            .WithPortBinding(1433, true)
            .Build();
    }

    public async Task InitializeAsync()
    {
        await _dbContainer.StartAsync();

        var port = _dbContainer.GetMappedPublicPort(1433);
        var connectionString = $"Server=localhost,{port};Database=TestDb;User Id=sa;Password=Your_password123;TrustServerCertificate=true";

        _connection = new SqlConnection(connectionString);
        await _connection.OpenAsync();

        // Run migrations
        await RunMigrationsAsync(_connection);
    }

    public async Task DisposeAsync()
    {
        await _connection.DisposeAsync();
        await _dbContainer.DisposeAsync();
    }

    [Fact]
    public async Task GetOrder_WithRealDatabase_ReturnsOrder()
    {
        // Arrange: Insert real test data
        await _connection.ExecuteAsync(
            "INSERT INTO Orders (Id, CustomerId, Total) VALUES (1, 'CUST1', 100.00)");

        var repo = new OrderRepository(_connection);

        // Act: Execute against real database
        var order = await repo.GetOrderAsync(1);

        // Assert: Verify actual database behavior
        Assert.NotNull(order);
        Assert.Equal(1, order.Id);
        Assert.Equal("CUST1", order.CustomerId);
        Assert.Equal(100.00m, order.Total);
    }
}

优势:

  • 测试真实的 SQL 查询和数据库行为
  • 发现约束违规、索引问题和性能问题
  • 验证迁移能否正常运行
  • 真正增强对数据访问层的信心

所需的 NuGet 包

<ItemGroup>
  <PackageReference Include="Testcontainers" Version="*" />
  <PackageReference Include="xunit" Version="*" />
  <PackageReference Include="xunit.runner.visualstudio" Version="*" />

  
  <PackageReference Include="Microsoft.Data.SqlClient" Version="*" />
  <PackageReference Include="Npgsql" Version="*" /> 
  <PackageReference Include="MySqlConnector" Version="*" /> 

  
  <PackageReference Include="StackExchange.Redis" Version="*" /> 
  <PackageReference Include="RabbitMQ.Client" Version="*" /> 
</ItemGroup>

入门

Testcontainers 库提供了一个简单的 API,可用于在测试中管理 Docker 容器。每项测试都可以启动所需的基础设施,Testcontainers 会自动处理其生命周期。

参考文档

如需了解详细模式和示例,请参阅参考文件:

最佳实践

  1. 始终使用 IAsyncLifetime——正确执行异步设置和清理
  2. 等待端口可用——使用 WaitStrategy 确保容器已就绪
  3. 使用随机端口——让 TestContainers 自动分配端口
  4. 在测试之间清理数据——使用全新的容器或清空表
  5. 尽可能复用容器——比每项测试都创建新容器更快
  6. 测试真实查询——不要只测试模拟对象;验证实际的 SQL 行为
  7. 验证约束——测试外键、唯一约束和索引
  8. 测试事务——验证回滚和提交行为
  9. 使用真实的数据——使用接近生产环境的数据量进行测试
  10. 处理清理工作——始终在 DisposeAsync 中释放容器

评论

支持 Markdown