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.
Use this skill when:
// 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.
// GOOD: Testing against a real database
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);
}
}
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.
<ItemGroup>
<PackageReference Include="Testcontainers" Version="*" />
<PackageReference Include="xunit" Version="*" />
<PackageReference Include="xunit.runner.visualstudio" Version="*" />
<!-- Database-specific packages -->
<PackageReference Include="Microsoft.Data.SqlClient" Version="*" />
<PackageReference Include="Npgsql" Version="*" /> <!-- For PostgreSQL -->
<PackageReference Include="MySqlConnector" Version="*" /> <!-- For MySQL -->
<!-- Other infrastructure -->
<PackageReference Include="StackExchange.Redis" Version="*" /> <!-- For Redis -->
<PackageReference Include="RabbitMQ.Client" Version="*" /> <!-- For RabbitMQ -->
</ItemGroup>
WaitStrategy to ensure containers are readyDisposeAsync_container = new TestcontainersBuilder<TestcontainersContainer>()
.WithImage("postgres:latest")
.WithWaitStrategy(Wait.ForUnixContainer()
.UntilPortIsAvailable(5432)
.WithTimeout(TimeSpan.FromMinutes(2)))
.Build();
Always use random port mapping:
.WithPortBinding(5432, true) // true = assign random public port
Ensure proper disposal:
public async Task DisposeAsync()
{
await _connection?.DisposeAsync();
await _container?.DisposeAsync();
}
Ensure CI has Docker support:
# GitHub Actions
runs-on: ubuntu-latest # Has Docker pre-installed
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
npx skills add Aaronontheweb/testcontainers-integration-tests下载完整 Skill 目录,包含 SKILL.md 及所有相关文件
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer