mcpField
T
testcontainers-integration-tests

testcontainers-integration-tests

github.com/aarononthewebテスト・品質1.2k555 回インストール今日更新
リポジトリ

testcontainers-integration-tests について

testcontainers-integration-tests は、モックではなくコンテナ内の実インフラを使う .NET 統合テストのスキルです。TestContainers 4.x に基づき、データベース、キャッシュ、メッセージキューのテストと、分離・後処理の方法を案内します。

主な機能

  1. 01コンテナ化された実インフラの利用
  2. 02SQL Server、PostgreSQL、Redis、RabbitMQ に対応
  3. 03分離とコンテナライフサイクルのガイダンス
  4. 04マイグレーションとデータベースリセットの例
  5. 05CI 構成とパフォーマンスの推奨事項

活用シーン

  1. 01実データベースに対するデータアクセスのテスト
  2. 02Redis または RabbitMQ 連携の検証
  3. 03マイグレーション、制約、トランザクションの確認
  4. 04Docker 対応 CI で統合テストを実行

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/aaronontheweb/dotnet-skills --skill testcontainers-integration-tests

インストール後、Claude Code で/testcontainers-integration-testsを入力して読み込まれたか確認します。

同梱ファイルdatabase-patterns.mdinfrastructure-patterns.md

詳細

TestContainers を使用した統合テスト

このスキルを使用するタイミング

このスキルは、次のような場合に使用します。

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

参照ファイル

基本原則

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

TestContainers 4.x API

このスキルでは、3.0 以降のモジュールビルダー API を備えた TestContainers 4.x を対象としています。3.0 より前の型は名前が変更され、削除されているため、TestcontainersBuilder<T> または TestcontainersContainer を使用するコードは、最新のパッケージではコンパイルできません。

旧(3.0 より前)新(3.0 以降)
new TestcontainersBuilder<T>()モジュールビルダー(例:new PostgreSqlBuilder(...))または new ContainerBuilder(...)
TestcontainersContainerDockerContainer/モジュールコンテナ(例:PostgreSqlContainer)
TestcontainersNetworkBuilderNetworkBuilder
GetMappedPublicPort(port)GetMappedPublicPort(port)(引き続き使用可能)

3.x ではなく、なぜ 4.x なのですか? Testcontainers 3.0.0 では、モジュールビルダーからイメージ指定コンストラクターを利用できませんでした。これらのコンストラクター(およびパラメーターなしのビルダーコンストラクターの非推奨化)は 4.x 系で追加されたため、ここでの例は 4.x の動作を前提としています。再現可能なビルドを実現するには、モジュールパッケージをバージョン未指定のワイルドカードではなく、4.x(またはそれ以降)のバージョンに固定してください。

推奨される方法は、Testcontainers.<Provider> パッケージのモジュールビルダーを使用することです(必要な NuGet パッケージを参照)。各モジュールビルダーでは、対象のデータベースまたはサービスに適したイメージ、環境変数、ポート、待機戦略があらかじめ設定されています。

非推奨のパラメーターなしコンストラクターを避けるには、.WithImage() ではなく、ビルダーのコンストラクターにイメージを渡します。

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

モックよりも 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 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);
    }
}

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

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

必要な NuGet パッケージ

モジュールパッケージをプロバイダーごとに 1 つ使用します。これらのパッケージを使うと、コアの Testcontainers パッケージに対して個別に処理を記述する必要がなくなります。

<ItemGroup>
  
  <PackageReference Include="Testcontainers" Version="*" />

  
  <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="*" />

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

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

必要な using 宣言

モジュールビルダーとコンテナ型は、プロバイダー固有の名前空間にあります。基本型(ContainerBuilder、NetworkBuilder、Wait)は DotNet.Testcontainers.Builders に、INetwork は DotNet.Testcontainers.Networks にあります。

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

ベストプラクティス

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

よくある問題と解決策

コンテナーの起動タイムアウト
_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 # Has Docker pre-installed

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 は取得したイメージをローカルにキャッシュします

コメント