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

testcontainers-integration-tests

Write integration tests using TestContainers for .NET with xUnit. Covers infrastructure testing with real databases, message queues, and caches in Docker containers instead of mocks.

インストール方法を見る

含まれるファイル(3)

  • SKILL.md9.5 KB
  • database-patterns.md5.4 KB
  • infrastructure-patterns.md10.4 KB

SKILL.md(原文)

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

Integration Testing with TestContainers

When to Use This Skill

Use this skill when:

  • Writing integration tests that need real infrastructure (databases, caches, message queues)
  • Testing data access layers against actual databases
  • Verifying message queue integrations
  • Testing Redis caching behavior
  • Avoiding mocks for infrastructure components
  • Ensuring tests work against production-like environments
  • Testing database migrations and schema changes

Reference Files

Core Principles

  1. Real Infrastructure Over Mocks - Use actual databases/services in containers, not mocks
  2. Test Isolation - Each test gets fresh containers or fresh data
  3. Automatic Cleanup - TestContainers handles container lifecycle and cleanup
  4. Fast Startup - Reuse containers across tests in the same class when appropriate
  5. CI/CD Compatible - Works seamlessly in Docker-enabled CI environments
  6. Port Randomization - Containers use random ports to avoid conflicts

TestContainers 4.x API

This skill targets TestContainers 4.x with the 3.0+ module builder API. The pre-3.0 types were renamed and removed, so code that uses TestcontainersBuilder<T> or TestcontainersContainer does not compile against the modern packages:

Old (pre-3.0)New (3.0+)
new TestcontainersBuilder<T>()Module builder (e.g. new PostgreSqlBuilder(...)) or new ContainerBuilder(...)
TestcontainersContainerDockerContainer / module container (e.g. PostgreSqlContainer)
TestcontainersNetworkBuilderNetworkBuilder
GetMappedPublicPort(port)GetMappedPublicPort(port) (still available)

Why 4.x and not 3.x? The module builders did not expose image-taking constructors in Testcontainers 3.0.0. Those constructors (and the deprecation of the parameterless builder constructor) arrived in the 4.x line, so the examples here rely on 4.x behavior. Pin the module packages to a 4.x (or newer) version rather than an unbounded wildcard if you want reproducible builds.

The recommended approach is the module builders from the Testcontainers.<Provider> packages (see Required NuGet Packages). Each module builder pre-configures the right image, environment variables, ports, and wait strategy for its database or service.

Pass the image to the builder constructor (not via .WithImage()) to avoid the obsolete parameterless constructor:

var container = new PostgreSqlBuilder("postgres:latest")
    .WithDatabase("testdb")
    .WithUsername("postgres")
    .WithPassword("postgres")
    .Build();

Why TestContainers Over Mocks?

The Problem with Mocking Infrastructure

// 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);
    }
}

Problems: doesn't test actual SQL queries, misses constraints/indexes, gives false confidence, doesn't catch SQL syntax errors.

Better: TestContainers with Real Database

// GOOD: Testing against a real database
public class OrderRepositoryTests : IAsyncLifetime
{
    private readonly MsSqlContainer _dbContainer;
    private SqlConnection _connection;

    public OrderRepositoryTests()
    {
        _dbContainer = new MsSqlBuilder("mcr.microsoft.com/mssql/server:2022-latest")
            .WithDatabase("TestDb")
            .WithPassword("Your_password123")
            .Build();
    }

    public async Task InitializeAsync()
    {
        await _dbContainer.StartAsync();
        _connection = new SqlConnection(_dbContainer.GetConnectionString());
        await _connection.OpenAsync();
        await RunMigrationsAsync(_connection);
    }

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

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

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

        Assert.NotNull(order);
        Assert.Equal("CUST1", order.CustomerId);
        Assert.Equal(100.00m, order.Total);
    }
}

See database-patterns.md for complete SQL Server, PostgreSQL, and migration testing examples.

See infrastructure-patterns.md for Redis, RabbitMQ, multi-container networks, container reuse, and Respawn database reset patterns.

Required NuGet Packages

Use the module packages — one per provider. They replace the special-casing you'd otherwise write against the core Testcontainers package:

<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>

Required Usings

The module builders and container types live in provider-specific namespaces. The base types (ContainerBuilder, NetworkBuilder, Wait) live in DotNet.Testcontainers.Builders, and INetwork lives in DotNet.Testcontainers.Networks:

using DotNet.Testcontainers.Builders;
using DotNet.Testcontainers.Networks;
using Testcontainers.MsSql;
using Testcontainers.PostgreSql;
using Testcontainers.Redis;
using Testcontainers.RabbitMq;

Best Practices

  1. Always Use IAsyncLifetime - Proper async setup and teardown
  2. Wait for Port Availability - Use a Wait strategy to ensure containers are ready
  3. Use Random Ports - Let TestContainers assign ports automatically
  4. Clean Data Between Tests - Either use fresh containers or truncate tables
  5. Reuse Containers When Possible - Faster than creating new ones for each test
  6. Test Real Queries - Don't just test mocks; verify actual SQL behavior
  7. Verify Constraints - Test foreign keys, unique constraints, indexes
  8. Test Transactions - Verify rollback and commit behavior
  9. Use Realistic Data - Test with production-like data volumes
  10. Handle Cleanup - Always dispose containers in DisposeAsync

Common Issues and Solutions

Container Startup Timeout

_container = new PostgreSqlBuilder("postgres:latest")
    .WithDatabase("testdb")
    .WithUsername("postgres")
    .WithPassword("postgres")
    .WithWaitStrategy(Wait.ForUnixContainer()
        .UntilInternalTcpPortIsAvailable(5432, w => w.WithTimeout(TimeSpan.FromMinutes(2))))
    .Build();

Port Already in Use

Always use random port mapping. Module builders handle this by default, but the core container builder exposes it explicitly:

.WithPortBinding(5432, true) // true = assign random public port

Containers Not Cleaning Up

Ensure proper disposal:

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

Tests Fail in CI But Pass Locally

Ensure CI has Docker support:

# GitHub Actions
runs-on: ubuntu-latest # Has Docker pre-installed

CI/CD Integration

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

Performance Tips

  1. Reuse containers - Share fixtures across tests in a collection
  2. Use Respawn - Reset data without recreating containers
  3. Parallel execution - TestContainers handles port conflicts automatically
  4. Use lightweight images - Alpine versions are smaller and faster
  5. Cache images - Docker will cache pulled images locally

レビュー

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

同じリポジトリのスキル

概要と使いどころ

Patterns for building entity actors with Akka.Hosting - GenericChildPerEntityParent, message extractors, cluster sharding abstraction, akka-reminders, and ITimeProvider. Supports both local testing and clustered production modes.

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

Aaronontheweb/dotnet-skills1,2182026年10月11日 更新

Configure Akka.NET with .NET Aspire for local development and production deployments. Covers actor system setup, clustering, persistence, Akka.Management integration, and Aspire orchestration patterns.

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

Aaronontheweb/dotnet-skills1,2182026年10月11日 更新

Critical Akka.NET best practices including EventStream vs DistributedPubSub, supervision strategies, error handling, Props vs DependencyResolver, work distribution patterns, and cluster/local mode abstractions for testability.

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

Aaronontheweb/dotnet-skills1,2182026年10月11日 更新

Akka.Management for cluster bootstrapping, service discovery (Kubernetes, Azure, Config), health checks, and dynamic cluster formation without static seed nodes.

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

Aaronontheweb/dotnet-skills1,2182026年10月11日 更新

Write unit and integration tests for Akka.NET actors using modern Akka.Hosting.TestKit patterns. Covers dependency injection, TestProbes, persistence testing, and actor interaction verification. Includes guidance on when to use traditional TestKit.

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

Aaronontheweb/dotnet-skills1,2182026年10月11日 更新

Guidelines for making .NET libraries and applications trimming-safe and Native AOT compatible. Covers the trimming/AOT model, the MSBuild properties that enable analysis (IsTrimmable, IsAotCompatible, PublishTrimmed, PublishAot), the trimming attributes (RequiresUnreferencedCode, RequiresDynamicCode, DynamicallyAccessedMembers, UnconditionalSuppressMessage), IL2xxx/IL3xxx warning codes, and a pattern playbook: source generators and UnsafeAccessor, generated-interception diagnostic suppressors, intentional runtime scanning boundaries, trimming-safe islands and feature switches, migration analyzers, and temporary warning-approval baselines. Use when introducing trimming/AOT support, resolving IL2xxx/IL3xxx warnings, or making reflection-heavy code trimming-safe in C# / .NET codebases. For System.Text.Json AOT scenarios, see also the serialization skill.

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

Aaronontheweb/dotnet-skills1,2182026年10月11日 更新

Aaronontheweb のスキルをすべて見る

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