## Description Migrate 17 Testcontainers guides from testcontainers.com into the Docker docs site, covering Java (14 guides), .NET (2 guides), and Node.js (1 guide). This follows up on PR #24450 which added the initial Go and Python guides. Each guide is converted from AsciiDoc to Hugo Markdown, split into multi-chapter stepper navigation, updated to the latest Testcontainers API, and verified with passing tests running in containers. Java guides use testcontainers-java 2.0.4 with the new 2.x Maven coordinates and package names (e.g., `testcontainers-postgresql`, `org.testcontainers.postgresql.PostgreSQLContainer`). The Quarkus guide uses Quarkus 3.22.3 with TC 1.x managed by the Quarkus BOM, since no released Quarkus version ships TC 2.x yet. ## How to test All code snippets have been verified by running each guide's source repository tests inside Docker containers with the Docker socket mounted. To re-run the verification, use the `/testcontainers-guides-migrator` skill included in this PR (`.claude/skills/testcontainers-guides-migrator/SKILL.md`). The skill's Step 6 documents the exact container commands and macOS Docker Desktop workarounds (host override, docker-java API version, etc.) needed to run each language's tests: ``` /testcontainers-guides-migrator I want you to verify all the guides in this branch. Do a full review, verifying that all code snippets compile, the code is executable, and ALL the tests pass. Run them as docker containers, never locally. ``` ## Related issues or tickets Supersedes #24450 (expanded from 2 guides to all 19) ## Reviews - [ ] Technical review - [ ] Editorial review - [ ] Product review --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6.6 KiB
title, linkTitle, description, weight
| title | linkTitle | description | weight |
|---|---|---|---|
| Write tests with Testcontainers | Write tests | Replace SQLite with a real Microsoft SQL Server using Testcontainers for .NET. | 20 |
The existing tests use an in-memory SQLite database. While convenient, this doesn't match production behavior. You can replace it with a real Microsoft SQL Server instance managed by Testcontainers.
Add dependencies
Change to the test project directory and add the SQL Server Entity Framework provider and the Testcontainers MSSQL module:
$ cd tests/RazorPagesProject.Tests
$ dotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 7.0.0
$ dotnet add package Testcontainers.MsSql --version 3.0.0
Note
Testcontainers for .NET offers a range of modules that follow best practice configurations.
Create the test class
Create a MsSqlTests.cs file in the IntegrationTests directory. This class
manages the SQL Server container lifecycle and contains a nested test class.
using System.Data.Common;
using System.Net;
using AngleSharp.Html.Dom;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using RazorPagesProject.Data;
using RazorPagesProject.Tests.Helpers;
using Testcontainers.MsSql;
using Xunit;
namespace RazorPagesProject.Tests.IntegrationTests;
public sealed class MsSqlTests : IAsyncLifetime
{
private readonly MsSqlContainer _msSqlContainer = new MsSqlBuilder().Build();
public Task InitializeAsync()
{
return _msSqlContainer.StartAsync();
}
public Task DisposeAsync()
{
return _msSqlContainer.DisposeAsync().AsTask();
}
public sealed class IndexPageTests : IClassFixture<MsSqlTests>, IDisposable
{
private readonly WebApplicationFactory<Program> _webApplicationFactory;
private readonly HttpClient _httpClient;
public IndexPageTests(MsSqlTests fixture)
{
var clientOptions = new WebApplicationFactoryClientOptions();
clientOptions.AllowAutoRedirect = false;
_webApplicationFactory = new CustomWebApplicationFactory(fixture);
_httpClient = _webApplicationFactory.CreateClient(clientOptions);
}
public void Dispose()
{
_webApplicationFactory.Dispose();
}
[Fact]
public async Task Post_DeleteAllMessagesHandler_ReturnsRedirectToRoot()
{
// Arrange
var defaultPage = await _httpClient.GetAsync("/")
.ConfigureAwait(false);
var document = await HtmlHelpers.GetDocumentAsync(defaultPage)
.ConfigureAwait(false);
// Act
var form = (IHtmlFormElement)document.QuerySelector("form[id='messages']");
var submitButton = (IHtmlButtonElement)document.QuerySelector("button[id='deleteAllBtn']");
var response = await _httpClient.SendAsync(form, submitButton)
.ConfigureAwait(false);
// Assert
Assert.Equal(HttpStatusCode.OK, defaultPage.StatusCode);
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
Assert.Equal("/", response.Headers.Location.OriginalString);
}
private sealed class CustomWebApplicationFactory : WebApplicationFactory<Program>
{
private readonly string _connectionString;
public CustomWebApplicationFactory(MsSqlTests fixture)
{
_connectionString = fixture._msSqlContainer.GetConnectionString();
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
services.Remove(services.SingleOrDefault(service => typeof(DbContextOptions<ApplicationDbContext>) == service.ServiceType));
services.Remove(services.SingleOrDefault(service => typeof(DbConnection) == service.ServiceType));
services.AddDbContext<ApplicationDbContext>((_, option) => option.UseSqlServer(_connectionString));
});
}
}
}
}
Understand the test structure
Container lifecycle with IAsyncLifetime
The outer MsSqlTests class implements IAsyncLifetime. xUnit calls
InitializeAsync() right after creating the class instance, which starts the
SQL Server container. After all tests complete, DisposeAsync() stops and
removes the container.
private readonly MsSqlContainer _msSqlContainer = new MsSqlBuilder().Build();
MsSqlBuilder().Build() creates a pre-configured Microsoft SQL Server
container. Testcontainers modules follow best practices, so you don't need
to configure ports, passwords, or startup wait strategies yourself.
Nested test class with IClassFixture
The IndexPageTests class is nested inside MsSqlTests and implements
IClassFixture<MsSqlTests>. This gives the test class access to the
container's private field and creates a clean hierarchy in the test explorer.
Custom WebApplicationFactory
Instead of using the SQLite-based factory, the nested
CustomWebApplicationFactory retrieves the connection string from the running
SQL Server container and passes it to UseSqlServer():
private sealed class CustomWebApplicationFactory : WebApplicationFactory<Program>
{
private readonly string _connectionString;
public CustomWebApplicationFactory(MsSqlTests fixture)
{
_connectionString = fixture._msSqlContainer.GetConnectionString();
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
services.Remove(services.SingleOrDefault(service => typeof(DbContextOptions<ApplicationDbContext>) == service.ServiceType));
services.Remove(services.SingleOrDefault(service => typeof(DbConnection) == service.ServiceType));
services.AddDbContext<ApplicationDbContext>((_, option) => option.UseSqlServer(_connectionString));
});
}
}
This factory:
- Removes the existing
DbContextOptions<ApplicationDbContext>registration - Removes the existing
DbConnectionregistration - Adds a new
ApplicationDbContextconfigured with the SQL Server connection string from the Testcontainers-managed container
Note
The Microsoft SQL Server Docker image isn't compatible with ARM devices, such as Macs with Apple Silicon. You can use the SqlEdge module or Testcontainers Cloud as alternatives.