Skip to content
Development
Skill

/testcontainers

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.

From plugin
dotnet-skills
1.2k39 skills6 agents

Claude Code

$ npx -y skills add aaronontheweb/dotnet-skills --skill testcontainers --agent claude-code

SKILL.md

testcontainers.SKILL.md
name: testcontainers-integration-tests
description: 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.
user-invocable: false

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

  • [database-patterns.md](database-patterns.md): SQL Server, PostgreSQL, and migration testing examples
  • [infrastructure-patterns.md](infrastructure-patterns.md): Redis, RabbitMQ, multi-container networks, container reuse, and Respawn

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(...)` | | `TestcontainersContainer` | `DockerContainer` / module container (e.g. `PostgreSqlContainer`) | | `TestcontainersNetworkBuilder` | `NetworkBuilder` | | `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](#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](database-patterns.md) for complete SQL Server, PostgreSQL, and migration testing examples.

See [infrastructure-patterns.md](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="Testcontainer
Read more
@aaronontheweb
Ships withdotnet-skills

A comprehensive AI coding plugin with 30 skills and 5 specialized agents for professional .NET development. Battle-tested patterns from production systems covering C#, Akka.NET, Aspire, EF Core, testing, and performance optimization.

1.2k39 skills6 agents
Get the whole plugin

Stars on GitHub

Momentum

stars in total
1.2k
Star history of aaronontheweb/dotnet-skills on GitHub, a chart by star-history.com

Other skills on dotnet-skills.

aot-trimming

aot-trimming

Guidelines for making .NET libraries and applications trimming-safe and Native AOT compatible. Covers the trimming/AOT model, the…

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills10d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc12d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react1d252k
@facebook@facebookInstall Skill

Questions about testcontainers.

Which coding agents can use testcontainers?
testcontainers installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.