Communityコーディング&開発github.com

testcontainers-integration-tests

TestContainers を使用して .NET アプリケーションの統合テストを記述します。モックではなく、Docker コンテナ内の実際のデータベース、メッセージキュー、キャッシュを使用したインフラテストをカバーします。

testcontainers-integration-tests とは?

このスキルは、TestContainers ライブラリを .NET と xUnit で活用した統合テストの作成方法を体系的に提供します。主な目的は、データベース(SQL Server、PostgreSQL、MySQL)、メッセージキュー(RabbitMQ)、キャッシュ(Redis)などのインフラコンポーネントをモックではなく実際の Docker コンテナで動作させることです。これにより、テストの信頼性が向上し、本番環境に近い状態での検証が可能になります。このスキルは、CI/CD パイプライン(GitHub Actions など)とのシームレスな統合をサポートし、コンテナのライフサイクル管理、ポートのランダム割り当て、テスト間の分離、自動クリーンアップなどのベストプラクティスを提供します。また、コンテナの再利用や Respawn を用いたデータリセットなど、パフォーマンス最適化のための戦略も含まれています。結果として、開発者は実際の SQL クエリ、制約、トランザクション、インデックスをテストし、モックでは発見できないバグを早期に検出できるようになります。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Aaronontheweb/dotnet-skills/tree/main/skills/testcontainers

Installed? Explore more コーディング&開発 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

TestContainers を使った統合テスト

このスキルを使うタイミング

以下の場合にこのスキルを使用します:

  • 実際のインフラ(データベース、キャッシュ、メッセージキュー)が必要な統合テストを記述する場合
  • 実際のデータベースに対してデータアクセス層をテストする場合
  • メッセージキュー統合を検証する場合
  • Redis キャッシュの動作をテストする場合
  • インフラコンポーネントのモックを避ける場合
  • 本番環境に近い環境でテストが動作することを確認する場合
  • データベースのマイグレーションとスキーマ変更をテストする場合

参照ファイル

  • database-patterns.md: SQL Server、PostgreSQL、およびマイグレーションテストの例
  • infrastructure-patterns.md: Redis、RabbitMQ、マルチコンテナネットワーク、コンテナ再利用、Respawn について

基本原則

  1. モックよりも実際のインフラ - モックではなく、コンテナ内の実際のデータベース/サービスを使用する
  2. テストの分離 - 各テストは新しいコンテナまたは新しいデータを取得する
  3. 自動クリーンアップ - TestContainers がコンテナのライフサイクルとクリーンアップを処理する
  4. 高速起動 - 適切な場合、同じクラス内のテスト間でコンテナを再利用する
  5. CI/CD 互換 - Docker が有効な CI 環境でシームレスに動作する
  6. ポートのランダム化 - コンテナは競合を避けるためにランダムなポートを使用する

モックよりも TestContainers を使う理由

インフラのモックの問題点

// 悪い例:データベースのモック
public class OrderRepositoryTests
{
    private readonly Mock<IDbConnection> _mockDb = new();

    [Fact]
    public async Task GetOrder_ReturnsOrder()
    {
        // これは実際の SQL の動作、制約、パフォーマンスをテストしない
        _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

// 良い例:実際のデータベースに対するテスト
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();
        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);
    }
}

完全な SQL Server、PostgreSQL、およびマイグレーションテストの例については database-patterns.md を参照してください。

Redis、RabbitMQ、マルチコンテナネットワーク、コンテナ再利用、および Respawn データベースリセットパターンについては infrastructure-patterns.md を参照してください。

必要な 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="*" /> <!-- PostgreSQL 用 -->
  <PackageReference Include="MySqlConnector" Version="*" /> <!-- MySQL 用 -->

  <!-- その他のインフラ -->
  <PackageReference Include="StackExchange.Redis" Version="*" /> <!-- Redis 用 -->
  <PackageReference Include="RabbitMQ.Client" Version="*" /> <!-- RabbitMQ 用 -->
</ItemGroup>

ベストプラクティス

  1. 常に IAsyncLifetime を使用する - 適切な非同期のセットアップとクリーンアップ
  2. ポートの利用可能性を待つ - WaitStrategy を使用してコンテナの準備ができるのを確認する
  3. ランダムなポートを使用する - TestContainers にポートを自動的に割り当てさせる
  4. テスト間でデータをクリーンアップする - 新しいコンテナを使用するか、テーブルを切り捨てる
  5. 可能な場合はコンテナを再利用する - テストごとに新しいコンテナを作成するより高速
  6. 実際のクエリをテストする - モックだけでなく、実際の SQL 動作を検証する
  7. 制約を検証する - 外部キー、一意制約、インデックスをテストする
  8. トランザクションをテストする - ロールバックとコミットの動作を検証する
  9. 現実的なデータを使用する - 本番に近いデータ量でテストする
  10. クリーンアップを処理する - 常に DisposeAsync でコンテナを破棄する

よくある問題と解決策

コンテナ起動のタイムアウト

_container = new TestcontainersBuilder<TestcontainersContainer>()
    .WithImage("postgres:latest")
    .WithWaitStrategy(Wait.ForUnixContainer()
        .UntilPortIsAvailable(5432)
        .WithTimeout(TimeSpan.FromMinutes(2)))
    .Build();

ポートがすでに使用中

常にランダムなポートマッピングを使用する:

.WithPortBinding(5432, true) // true = ランダムな公開ポートを割り当て

コンテナがクリーンアップされない

適切な破棄を確保する:

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

パフォーマンスのヒント

  1. コンテナを再利用する - コレクション内のテスト間でフィクスチャを共有する
  2. Respawn を使用する - コンテナを再作成せずにデータをリセットする
  3. 並列実行 - TestContainers はポート競合を自動的に処理する
  4. 軽量イメージを使用する - Alpine バージョンは小さくて高速
  5. イメージをキャッシュする - Docker はプルしたイメージをローカルにキャッシュする

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

akka-hosting-actor-patterns

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.

akka-net-aspire-configuration

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.

akka-net-best-practices

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.

akka-net-management

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

akka-net-testing-patterns

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.

api-design

Design stable, compatible public APIs using extend-only design principles. Manage API compatibility, wire compatibility, and versioning for NuGet packages and distributed systems.

aspire-configuration

Configure Aspire AppHost to emit explicit app config via environment variables; keep app code free of Aspire clients and service discovery.

aspire-integration-testing

Write integration tests using .NET Aspire

aspire-service-defaults

Create a shared ServiceDefaults project for Aspire applications. Centralizes OpenTelemetry, health checks, resilience, and service discovery configuration across all services.

crap-analysis

Analyze code coverage and CRAP (Change Risk Anti-Patterns) scores to identify high-risk code. Use OpenCover format with ReportGenerator for Risk Hotspots showing cyclomatic complexity and untested code paths.

csharp-concurrency-patterns

Choosing the right concurrency abstraction in .NET - from async/await for I/O to Channels for producer/consumer to Akka.NET for stateful entity management. Avoid locks and manual synchronization unless absolutely necessary.

database-performance

Database access patterns for performance. Separate read/write models, avoid N+1 queries, use AsNoTracking, apply row limits, and never do application-side joins. Works with EF Core and Dapper.

dependency-injection-patterns

Organize DI registrations using IServiceCollection extension methods. Group related services into composable Add* methods for clean Program.cs and reusable configuration in tests.

dotnet-devcert-trust

Diagnose and fix .NET HTTPS dev certificate trust issues on Linux. Covers the full certificate lifecycle from generation to system CA bundle inclusion, with distro-specific guidance for Ubuntu, Fedora, Arch, and WSL2.

dotnet-local-tools

Managing local .NET tools with dotnet-tools.json for consistent tooling across development environments and CI/CD pipelines.

dotnet-project-structure

Modern .NET project structure including .slnx solution format, Directory.Build.props, central package management, SourceLink, version management with RELEASE_NOTES.md, and SDK pinning with global.json.

dotnet-slopwatch

Use Slopwatch to detect LLM reward hacking in .NET code changes. Run after every code modification to catch disabled tests, suppressed warnings, empty catch blocks, and other shortcuts that mask real problems.

efcore-patterns

Entity Framework Core best practices including NoTracking by default, query splitting for navigation collections, migration management, dedicated migration services, and common pitfalls to avoid.

ilspy-decompile

Understand implementation details of .NET code by decompiling assemblies. Use when you want to see how a .NET API works internally, inspect NuGet package source, view framework implementation, or understand compiled .NET binaries.

mailpit-integration

Test email sending locally using Mailpit with .NET Aspire. Captures all outgoing emails without sending them. View rendered HTML, inspect headers, and verify delivery in integration tests.

関連スキル