TestFramework.Container 0.4.0

The owner has unlisted this package. This could mean that the package is deprecated, has security vulnerabilities or shouldn't be used anymore.
dotnet add package TestFramework.Container --version 0.4.0
                    
NuGet\Install-Package TestFramework.Container -Version 0.4.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="TestFramework.Container" Version="0.4.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="TestFramework.Container" Version="0.4.0" />
                    
Directory.Packages.props
<PackageReference Include="TestFramework.Container" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add TestFramework.Container --version 0.4.0
                    
#r "nuget: TestFramework.Container, 0.4.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package TestFramework.Container@0.4.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=TestFramework.Container&version=0.4.0
                    
Install as a Cake Addin
#tool nuget:?package=TestFramework.Container&version=0.4.0
                    
Install as a Cake Tool

Icon

TestFramework.Container

Shared Docker building blocks for TestFramework container environments.

This package holds what every container-backed environment needs and nothing specific to any one of them. TestFramework.Container.Azure and the web container environment are both built on it.

You normally consume one of those packages rather than this one directly. Reach for it when writing an environment component of your own.

Install

dotnet add package TestFramework.Container

Targets net8.0 and net10.0.

What It Provides

Type Purpose
ContainerSource declares where an application comes from: an image, a project, a directory, a type
ContainerSourcePlan what will be done to get it there, stated before anything happens
ContainerImageBuilder carries a plan out and produces an image or a directory
ProjectQuery asks MSBuild what a project is, instead of inferring it from paths
OfflineFeed hands a container the packages the host restored, so a build needs no credentials
ContainerNetworkFactory creates the uniquely named network one environment's containers share
ContainerEndpoints the two addresses every container has: host-mapped and network-alias
ContainerReadiness waits until an HTTP endpoint or a SQL database actually answers
ContainerOutputResolver answers which framework an assembly was built for
ContainerLogCapture writes a container's output into the run log before it is removed
ContainerDockerCommands runs the Docker CLI and removes containers and networks reliably
ContainerDockerHost points the client at the Docker Desktop named pipe a Windows machine uses
ContainerRuntime does that once per process, and both network components call it first
ContainerPortBinding publishes ports on 127.0.0.1 when the daemon is on this machine
ContainerStartCoordinator starts one component's containers at once and cleans up a failed batch
MsSqlContainerFactory builds a SQL Server container from shared settings

Three Things That Happen Without Being Asked

ContainerRuntime.EnsureInitialized runs as the first statement of the network component in both the Azure and the Web environment, which is the root every other component depends on. On Windows it points DOCKER_HOST at whichever Docker Desktop named pipe exists, because the client library does not probe for it and the name differs between installations. It changes nothing when DOCKER_HOST is already set, and it logs the pipe it chose.

ContainerPortBinding publishes every port on 127.0.0.1 rather than on all interfaces, so a SQL Server or a storage emulator a test brought up is not reachable from the rest of the network for the length of the run. It falls back to 0.0.0.0 when the daemon is remote or in Docker-in-Docker, where the test process reaches the container over the network. Set TESTFRAMEWORK_CONTAINER_HOST_IP to decide explicitly.

ContainerLeftovers.SweepAsync starts from the same place, detached, and removes what a killed test host leaves behind. Ryuk reaps containers, networks and volumes and nothing else, so a run that is killed rather than torn down leaves its built images, its published output and its generated emulator topology on the machine with nothing that knows to come back for them.

It removes, and only removes:

  • images labelled com.testframework.container=true (built from a generated Dockerfile) or org.opencontainers.image.vendor=testframework-container (published by the SDK), created more than 24 hours ago
  • directories named tf-* directly under the temp directory, last written more than 24 hours ago
  • *.json under %TEMP%/TestFramework/servicebus-topologies, last written more than 24 hours ago

Two things are worth knowing. docker image prune --filter until= measures the image's creation time, not its last use, so an image built by a run that has already lasted more than a day is eligible while that run is still going; Docker refuses to remove an image a running container came from, so the prune skips it rather than breaking the run. And an in-container build deliberately keeps its context after a failure so the generated Dockerfile and offline feed can be inspected — the expiry is what eventually clears those, because the code that decided to keep the context is the code that just failed.

The sweep never throws, never delays a run, and never looks outside the two names above. Set TESTFRAMEWORK_CONTAINER_NO_SWEEP to any value to turn it off.

The One Rule Worth Knowing

A container has two addresses, and using the wrong one is the most common defect in a container-backed test setup:

// The test process reaches the container through the mapped port on the Docker host.
Uri fromTest = ContainerEndpoints.HostEndpoint(container, 8080);

// Another container on the same network reaches it by alias and internal port.
Uri fromContainer = ContainerEndpoints.NetworkEndpoint("orders-api", 8080);

The same applies to connection strings: publish HostSqlConnectionString(...) to the test process, and inject NetworkSqlConnectionString(...) into the application container's settings. Both describe the same database.

Readiness Is Not Startup

A started container is not a usable one. Components wait before publishing an endpoint, so a startup race surfaces as a clear timeout rather than as a confusing failure in the first step that uses it:

await ContainerReadiness.WaitForHttpAsync(baseAddress, "/health", TimeSpan.FromMinutes(2), "orders", logger, cancellationToken);
await ContainerReadiness.WaitForSqlStatementAsync(connectionString, "SELECT DB_NAME();", TimeSpan.FromMinutes(2), "main", cancellationToken);

Getting An Application Into A Container

An application's source is declared, not discovered:

ContainerSource.Image("orders-api:ci-1234")                     // already an image
ContainerSource.Project("../Orders.Api/Orders.Api.csproj")      // the framework builds it
ContainerSource.Directory(@"C:\out\orders-api")                 // this exact folder

A relative project path resolves against the source file that declares it, captured at compile time, so it reads the way it looks in the repository rather than depending on a working directory.

ContainerSource.Project(...) needs no reference from the test project to the application, which is what keeps the application a black box. Everything else is read from the project by MSBuild — target framework, assembly name, whether it uses the web SDK, its project references — so a custom output path or the artifacts/ layout costs nothing.

Three ways to build a project

ContainerSource.Project(path)                 // .BuiltAsImage() — the default
ContainerSource.Project(path).BuiltOnHost()
ContainerSource.Project(path).BuiltInContainer()
Strategy How Needs Host artifacts
BuiltAsImage the SDK builds the image (-t:PublishContainer), no Dockerfile SDK + a docker executable bin/obj
BuiltOnHost publishes to a temp directory, copied into a runtime image SDK bin/obj + temp, deleted after
BuiltInContainer a generated Dockerfile builds it in Docker SDK for the restore, a daemon none

BuiltInContainer needs no feed credentials. The host restores with the configuration that already works; the packages that produced are copied into the build context as an extracted cache, and the generated NuGet.config clears every source. The build cannot reach a feed and cannot resolve anything the host did not.

It carries one constraint, checked while planning rather than left to fail inside the container: the target framework generation must match the SDK on the machine, because which packages count as framework-provided is SDK-version-dependent, and a mismatch asks for packages the handed-over cache does not contain.

The plan is stated before anything happens

ContainerSourcePlan plan = await ContainerSourceResolver.PlanAsync(source, cancellationToken);
foreach (string line in plan.ToLogLines("orders"))
    logger.LogInformation(line);
'orders' source plan
  kind            Project (image built by the SDK)
  project         C:\src\Orders.Api\Orders.Api.csproj
  configuration   Release
  framework       net10.0
  runtime         mcr.microsoft.com/dotnet/aspnet:10.0
  derived         framework from the project's single target, 'net10.0'
  derived         runtime image from the project using the web SDK

Planning has no side effects — nothing is built, pulled or started — so "what would this run" is answerable without waiting for Docker. Values that were worked out rather than declared say so.

A project that targets several frameworks is a hard error, not a silent pick: adding a framework to a project must not quietly change what a test runs.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated

Host publishes and SDK image builds of one project are now serialized across processes, not only within one: a machine-wide lock backs the per-project gate, closing the WorkerExtensions build race a multi-targeted test suite hits when its frameworks run as parallel processes. Builds also run without the shared Roslyn compiler server, whose lingering reference handles could block the next publish of the same Function App.