TestFramework.Core
0.6.0
dotnet add package TestFramework.Core --version 0.6.0
NuGet\Install-Package TestFramework.Core -Version 0.6.0
<PackageReference Include="TestFramework.Core" Version="0.6.0" />
<PackageVersion Include="TestFramework.Core" Version="0.6.0" />
<PackageReference Include="TestFramework.Core" />
paket add TestFramework.Core --version 0.6.0
#r "nuget: TestFramework.Core, 0.6.0"
#:package TestFramework.Core@0.6.0
#addin nuget:?package=TestFramework.Core&version=0.6.0
#tool nuget:?package=TestFramework.Core&version=0.6.0
TestFramework.Core
TestFramework.Core is the timeline engine of the TestFramework ecosystem.
It provides the public API to:
- define integration-test workflows
- execute them with runtime inputs
- assert outcomes from an immutable run result
Install
dotnet add package TestFramework.Core
Quick Start
using TestFramework.Core.Timelines;
using TestFramework.Core.Timelines.Assertions;
using TestFramework.Core.Variables;
using Xunit;
public class CoreSample
{
private const string InputValue = "Alex";
private static readonly Timeline _timeline = Timeline.Create()
.SetVariable("name", Var.Const(InputValue))
.Transform("greeting", Var.Ref<string>("name"), name => $"Hello {name}")
.AssertVariable(Var.Ref<string>("greeting"), greeting => greeting == $"Hello {InputValue}")
.Build();
[Fact]
public async Task RunTimeline()
{
TimelineRun run = await _timeline.SetupRun().RunAsync();
run.EnsureRanToCompletion();
using (var assertionScope = run.AssertionScope())
{
run.Variable<string>("greeting").Should().Exist().And().Be($"Hello {InputValue}");
}
}
}
Common Building Blocks
Timeline.Create()to start the builderSetVariable,Transform,AssertVariablefor variable-driven data flowTrigger(...)andWaitForEvent(...)for actions and external synchronizationWithTimeOut(...),WithRetry(...)for reliability on unstable systems
Consumer-First Contract
For most users, the Core contract is intentionally small:
- Start with
Timeline.Create(). - Compose fluent steps and modifiers.
- Freeze the plan with
Build(). - Create a run with
SetupRun(...). - Execute with
RunAsync()and assert throughTimelineRun.
The scope split matters:
Build()usually belongs at class scope because it produces the reusable timeline definition.SetupRun(...)usually belongs at method scope because each call creates a per-run builder with run-specific services, variables, artifacts, or output wiring.
The package exposes additional public types for artifacts, environment integration, debugging, and the fluent builder composition model, but those are advanced surfaces. If you are writing tests rather than framework extensions, prefer the timeline builder, Var, TimelineRun, and the assertion handles as your main API.
Fluent API Discovery
The fluent API is one logical surface even though it is composed internally from several public interfaces.
For normal usage, treat these as the only concepts that matter:
Timeline.Create()starts composition- fluent builder verbs add steps and modifiers
Build()freezes the reusable definitionSetupRun(...)creates a per-run builderRunAsync()executes the run
The lower-level action interfaces remain public for compatibility and extension reasons, but they are intentionally de-emphasized in IntelliSense. If you discover those types directly, prefer returning to ITimelineBuilder, ITimelineBuilderModifier, and the fluent usage examples rather than learning the API through the interface lattice.
Extension-Facing Surface
You only need the lower-level public abstractions when you are extending the framework itself, for example by adding:
- custom triggers or events
- artifact describers and references
- environment-provider integrations
- runtime or debugging integrations
Those advanced surfaces are supported by the architecture docs, but they are secondary to the consumer workflow above.
Timeline Debugging
The recommended debugging path depends on what you need to see:
- Name important steps so failures and assertions point to stable labels.
- Pass
ITestOutputHelperintoSetupRun(...)when you want the timeline log in the test output stream. - Inspect the completed
TimelineRunfor stage state, step results, variables, and artifacts.
For most users, that post-run inspection path is the supported debugging workflow.
The lower-level debugger integration seam (IRunDebugger and related state types) remains available for custom tooling, but it is an advanced integration surface rather than the primary learning path.
Run Evidence (Widgets)
Post-run inspection tells you what a run did. run.Widgets is where it says what things looked like:
run.Widgets.Publish(new Widget
{
Kind = WidgetKinds.Screenshot,
Name = "after-login",
Form = DebugPreviewForm.Image,
Bytes = png,
Summary = "The dashboard, logged in"
});
The file lands in the run's own output and is attributed to the step and the attempt that produced it, so
retries keep every attempt rather than only the last. Publish hands back where it wrote the file, or
null if it could not, and never throws - failing to gather evidence must not add a second failure. WidgetKinds names the three the family produces -
Screenshot, Document, LogStream - and the kind is a plain string, so a package can introduce one
without waiting on a Core release.
For a run that is held at a breakpoint, register an IWidgetCaptureSource: the engine asks it for a
current picture while the run is stopped, which the last captured one no longer is.
This is the supported place for evidence. A package that writes screenshots into a folder of its own making has an arrangement only it and one tool understand.
Typical Pattern
- Build timeline once (usually static in test classes or other reusable class scope).
- Create a run with
SetupRun(...)inside the test or method that is about to execute it. - Add runtime variables/artifacts if needed.
- Run with
RunAsync(). - Assert with
EnsureRanToCompletion()and variable/artifact checks.
Persistent Environments
Most timelines should keep environment creation per run.
When a suite repeatedly needs the same expensive environment slice, Core also exposes PersistentEnvironmentContext<TSetup> as the lower-level reuse primitive.
Use it when all of the following are true:
- the environment shape is stable across many runs
- some components are expensive enough that recreating them dominates runtime
- those components can safely opt into
EnvComponentReuseMode.PersistentContext
The model is:
TSetup.CreateEnvironment()describes the full environment instance that future runs should receive.TSetup.GetPersistentComponentIdentifiers()selects the component roots that should be realized once and reused.PersistentEnvironmentContext<TSetup>.CreateEnvironment()produces fresh run environments with the persistent runtime state seeded back in.
Higher-level packages may wrap this primitive with package-specific helpers. In the container stack, DockerAzureHostedCollectionFixture<TState> is the xUnit-facing example of that pattern.
Target Frameworks
- .NET 8 (
net8.0) - .NET 10 (
net10.0)
| Product | Versions 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. |
-
net10.0
- Newtonsoft.Json (>= 13.0.4)
- xunit.abstractions (>= 2.0.3)
-
net8.0
- Newtonsoft.Json (>= 13.0.4)
- xunit.abstractions (>= 2.0.3)
NuGet packages (4)
Showing the top 4 NuGet packages that depend on TestFramework.Core:
| Package | Downloads |
|---|---|
|
TestFramework.Container.Azure
Docker-backed environment providers for TestFramework timelines for Azure components. |
|
|
TestFramework.Container.Web
Docker-backed environment providers for TestFramework.Web timelines: builds and runs the application under test, starts the SQL Server behind it and the stubbed dependencies in front of it, and publishes every address into the running timeline. |
|
|
TestFramework.UI.Browser
Drive a local browser from inside a TestFramework timeline. Interactions read as imperative steps (Navigate, Click, Fill, Expect) and address elements the way a person does - by role, label, placeholder or visible text - so a renamed or relocated control does not fail a test that only wanted to press it. Every non-exact match is recorded and assertable, ambiguity fails with the candidates and the dial that resolves it, and expected page structure is declared as a typed, reusable field. |
|
|
TestFramework.Mock
Test the system under test in-process from inside a TestFramework timeline, with its own dependencies replaced by declared test doubles. Mock-Packs declare a double once; MockEnvironment hosts the application's real service composition with the doubles standing in; MockExt.Host calls into it, awaiting async calls; and what the doubles record becomes findable run artifacts, verified from the frozen run. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.6.0 | 55 | 9/30/2026 |
BREAKING - every step requirement must name a resource the run declares (in configuration, a fixture or an environment). It is checked while the run is planned, and a requirement nothing declares is refused before the first step instead of failing inside it. An environment now receives only the requirements that are its to provide: resources it declares itself, and every resource of a kind it maps with MapResourceKind. MapDeclaredResources maps a kind only for what the environment declares, EnvironmentResources.Required hands each component what the run needs, and OnRequirementResolved is obsolete.