
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.
- Installs
- 550
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/aaronontheweb/dotnet-skills --skill testcontainers-integration-tests- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads testcontainers-integration-tests automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
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: SQL Server, PostgreSQL, and migration testing examples
- infrastructure-patterns.md: Redis, RabbitMQ, multi-container networks, container reuse, and Respawn
Core Principles
- Real Infrastructure Over Mocks - Use actual databases/services in containers, not mocks
- Test Isolation - Each test gets fresh containers or fresh data
- Automatic Cleanup - TestContainers handles container lifecycle and cleanup
- Fast Startup - Reuse containers across tests in the same class when appropriate
- CI/CD Compatible - Works seamlessly in Docker-enabled CI environments
- 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). 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
- Always Use IAsyncLifetime - Proper async setup and teardown
- Wait for Port Availability - Use a
Waitstrategy to ensure containers are ready - Use Random Ports - Let TestContainers assign ports automatically
- Clean Data Between Tests - Either use fresh containers or truncate tables
- Reuse Containers When Possible - Faster than creating new ones for each test
- Test Real Queries - Don't just test mocks; verify actual SQL behavior
- Verify Constraints - Test foreign keys, unique constraints, indexes
- Test Transactions - Verify rollback and commit behavior
- Use Realistic Data - Test with production-like data volumes
- 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
- Reuse containers - Share fixtures across tests in a collection
- Use Respawn - Reset data without recreating containers
- Parallel execution - TestContainers handles port conflicts automatically
- Use lightweight images - Alpine versions are smaller and faster
- Cache images - Docker will cache pulled images locally
FAQ
What does the testcontainers-integration-tests skill do?
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.
How do I install testcontainers-integration-tests?
Run npx skills add https://github.com/aaronontheweb/dotnet-skills --skill testcontainers-integration-tests in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/testcontainers-integration-tests/ (personal) or .claude/skills/testcontainers-integration-tests/ (project).
Which AI agents can use testcontainers-integration-tests?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is testcontainers-integration-tests safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains testcontainers-integration-tests?
It is published by aaronontheweb in aaronontheweb/dotnet-skills. It has 550 recorded installs.
More from aaronontheweb/dotnet-skills
modern-csharp-coding-standards
aaronontheweb/dotnet-skills
Write modern, high-performance C code using records, pattern matching, value objects, async/await, Span /Memory , and best-practice API design patterns. Emphasizes functional-style programming with C 12+ features.
csharp-concurrency-patterns
aaronontheweb/dotnet-skills
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.
efcore-patterns
aaronontheweb/dotnet-skills
Entity Framework Core best practices including NoTracking by default, query splitting for navigation collections, migration management, dedicated migration services, and common pitfalls to avoid.
dependency-injection-patterns
aaronontheweb/dotnet-skills
Organize DI registrations using IServiceCollection extension methods. Group related services into composable Add methods for clean Program.cs and reusable configuration in tests.
database-performance
aaronontheweb/dotnet-skills
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.
api-design
aaronontheweb/dotnet-skills
Design stable, compatible public APIs using extend-only design principles. Manage API compatibility, wire compatibility, and versioning for NuGet packages and distributed systems.
Popular testing & qa skills
tdd
mattpocock/skills
Test-driven development. Use when the user wants to build features or fix bugs test-first, mentions "red-green-refactor", or wants integration tests.
grilling
mattpocock/skills
Grill the user relentlessly about a plan, decision, or idea. Use when the user wants to stress-test their thinking, or uses any 'grill' trigger phrases.
migrate-to-shoehorn
mattpocock/skills
Migrate test files from as type assertions to @total-typescript/shoehorn. Use when user mentions shoehorn, wants to replace as in tests, or needs partial test data.
run-train
lllllllama/rigorpilot-skills
Rigor Train skill for deep learning research repositories. Use when a documented or selected training command should be run conservatively for startup verification, short-run verification, full kickoff, or resume, with command, config, seed, log, checkpoint, status, and metric evidence written to standardized trainoutputs/. Do not use for environment setup, exploratory sweeps, speculative idea implementation, or end-to-end orchestration.
test-driven-development
obra/superpowers
Use when implementing any feature or bugfix, before writing implementation code
finishing-a-development-branch
obra/superpowers
Use when implementation is complete, all tests pass, and you need to decide how to integrate the work