Skip to content

Using it in your tests (auto)

Experimental Containers Reviewed 2026-10-01 purview-dev/containers Star on GitHub 3 azurite containers csharp docker dotnet integration-testing mysql nats nuget postgresql rabbitmq redis sql-server testcontainers testing windows wsl wsl-containers wslc

Experimental. This project is an experiment: the public API, defaults and packaging can change between prereleases, and there is no production support guarantee.

The zero-configuration shape: one project, no backend choice in your code or environment. The same tests run on WSL Containers on a Windows developer machine and on Docker on a Linux CI runner (or a Mac) — nothing changes between them.

<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<!-- the services you want -->
<PackageReference Include="Purview.Containers.Redis" Version="1.0.0-prerelease.2" />
<PackageReference Include="Purview.Containers.PostgreSql" Version="1.0.0-prerelease.2" />
<!-- one backend reference: the umbrella brings the abstractions and both backends -->
<PackageReference Include="Purview.Containers" Version="1.0.0-prerelease.2" />
</ItemGroup>
</Project>

Three things make this work, and nothing else is required:

  • A platform-neutral target framework (net10.0). Purview.Containers.Wsl is multi-target; a net10.0 project binds its portable facade, which runs the WSLC implementation on Windows and reports wsl unavailable everywhere else. (A Windows target framework still works — it binds the implementation directly — but it cannot run on a Linux CI runner.)
  • The umbrella package (Purview.Containers), which brings Purview.Containers.Core (the abstractions) and both backends. WSLC is preferred where it is usable and Docker is the fallback, so the one project runs everywhere. Prefer to be explicit? Reference Purview.Containers.Core plus Purview.Containers.Wsl and/or Purview.Containers.Docker individually — a Linux-only CI job can skip the WSL backend and its ~19 MB payload with Core + .Docker only.
  • No registration line, no environment variable. Each backend package ships buildTransitive assets that generate a module initializer in your assembly, so the process discovers the registered backends by itself. Those assets flow transitively through the umbrella.
using Npgsql;
using Purview.Containers.PostgreSql;
using Purview.Containers.Redis;
using StackExchange.Redis;
public class CacheAndDatabaseTests
{
[Test]
public async Task PostgreSql_is_usable()
{
await using var postgres = new PostgreSqlBuilder().WithDatabase("app").Build();
await postgres.StartAsync(); // returns once pg_isready succeeds
await using var connection = new NpgsqlConnection(postgres.GetConnectionString());
await connection.OpenAsync();
// ...your assertions against a real database...
}
[Test]
public async Task Redis_is_usable()
{
await using var redis = new RedisBuilder().Build();
await redis.StartAsync(); // returns once redis-cli ping succeeds
await using var cache = await ConnectionMultiplexer.ConnectAsync(redis.GetConnectionString());
await cache.GetDatabase().PingAsync();
// ...your assertions against a real cache...
}
}

That is the whole story: no image names, no ports, no Testcontainers, no docker, no wslc. The builder default image, the port mapping (a random host port), and the readiness check all come from the module; GetConnectionString() points at the mapped host port.

HostSelected backendWhy
Windows with WSL ContainerswslThe facade loads the WSLC implementation and WSLC is preferred.
Windows without WSL Containers, with DockerdockerThe facade reports wsl unavailable, so selection falls through.
Linux / macOS (any CI runner)dockerThe facade only ever activates on Windows.

Automatic selection probes the registered backends in priority order (wsl is 0, docker is 100) and returns the first one that is both available and compatible. When none is usable, the exception lists each backend’s availability, version and missing components — see Backends: WSLC or Docker.

Auto is the default and needs no configuration. Two things help while debugging:

// Which backends can this machine run, and what version?
foreach (var backend in await ContainerBackends.ProbeAllAsync())
{
Console.WriteLine($"{backend.Name}: usable={backend.IsUsable} version={backend.Version}");
}
Terminal window
# Fail loudly instead of falling back (the usual choice in CI).
PURVIEW_CONTAINERS_BACKEND=docker # or: wsl

A named backend never silently falls back: if it cannot run, the test fails with that backend’s own diagnostics.

  • The abstractions and every service module are portable net10.0; see Consumer Requirements for the exact target-framework contract, the PCC0001/PCC0002 guards and the EnableWindowsTargeting workaround for non-Windows build agents.
  • The per-module connection-string shapes (Redis, PostgreSQL, SQL Server, MySQL, RabbitMQ, Azurite, NATS) are in Modules.
  • A live sample of this shape is samples/getting-started/AutoSample (just sample-auto).