TestFramework.Core 0.5.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.Core --version 0.5.0
                    
NuGet\Install-Package TestFramework.Core -Version 0.5.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.Core" Version="0.5.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="TestFramework.Core" Version="0.5.0" />
                    
Directory.Packages.props
<PackageReference Include="TestFramework.Core" />
                    
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.Core --version 0.5.0
                    
#r "nuget: TestFramework.Core, 0.5.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.Core@0.5.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.Core&version=0.5.0
                    
Install as a Cake Addin
#tool nuget:?package=TestFramework.Core&version=0.5.0
                    
Install as a Cake Tool

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 builder
  • SetVariable, Transform, AssertVariable for variable-driven data flow
  • Trigger(...) and WaitForEvent(...) for actions and external synchronization
  • WithTimeOut(...), WithRetry(...) for reliability on unstable systems

Consumer-First Contract

For most users, the Core contract is intentionally small:

  1. Start with Timeline.Create().
  2. Compose fluent steps and modifiers.
  3. Freeze the plan with Build().
  4. Create a run with SetupRun(...).
  5. Execute with RunAsync() and assert through TimelineRun.

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 definition
  • SetupRun(...) creates a per-run builder
  • RunAsync() 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:

  1. Name important steps so failures and assertions point to stable labels.
  2. Pass ITestOutputHelper into SetupRun(...) when you want the timeline log in the test output stream.
  3. Inspect the completed TimelineRun for 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

  1. Build timeline once (usually static in test classes or other reusable class scope).
  2. Create a run with SetupRun(...) inside the test or method that is about to execute it.
  3. Add runtime variables/artifacts if needed.
  4. Run with RunAsync().
  5. 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:

  1. TSetup.CreateEnvironment() describes the full environment instance that future runs should receive.
  2. TSetup.GetPersistentComponentIdentifiers() selects the component roots that should be realized once and reused.
  3. 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 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

New in 0.5.0: a run has somewhere to put the evidence it produced. RunContext.Widgets takes a screenshot, a log or a generated file, writes it into the run's own output, and attributes it to the step and the attempt that produced it - so a step that retried has one picture per attempt rather than only the last. A watching consumer can also ask a paused run for a fresh look at itself: a caller registers an IWidgetCaptureSource, the engine asks it while the run is held, and the answer waits until the widgets it counts have actually been delivered. A debugger receives them through ISupportsWidgets, which is a second interface rather than a wider IRunDebugger, so a debugger compiled against an earlier Core keeps working and simply does not receive them.
Also new in 0.5.0: ArtifactStore.CaptureVersion is public, so a package's own step can record a new version of an artifact with data the step already holds - captured at a boundary the run drove, with nothing for a reference to re-resolve. The write goes through the same funnel as Add, and being public adds two refusals: an instance this store does not hold, and version data of a foreign artifact kind. And a test marked with an attribute derived from a test attribute is identified as that test rather than as the host process, which used to cost such a suite its breakpoints, its baseline comparison and its re-run filter.
CHANGED in 0.5.0, and worth a look if you already record runs: the run journal now resolves under %USERPROFILE%\.testframework\Debug instead of %LOCALAPPDATA%\TestFramework\Debug. The existence of that folder is what switches recording on, so an existing installation records nothing until the tool creates the new one - reinstalling or starting the current launcher does it. Journals already written stay where they are; move them across if you want them, or point TESTFRAMEWORK_DEBUG_JOURNAL_DIR at the old path. The move is what keeps the folder usable as a handshake between two processes regardless of how either is packaged, which a path under AppData cannot promise.
Earlier: BREAKING (behaviour, introduced in 0.3.0 - whose release notes did not say so): teardown deletes every artifact a run declared, registered or discovered, unless the declaring step chains MarkReadonly(). Deleting is the default because a test that leaves its own data behind poisons the next run; MarkReadonly() is the single opt-out, it is stated where the artifact is declared, and nothing downstream can overrule it.
What changed for a consumer: discovery used to be safe by accident. Some finders returned references teardown could not delete - LocalIO's folder finder, the web package's SQL row finder, Azure's EF Core SQL finder - while Cosmos and Table discovery deleted. Now all of them delete unless told otherwise, so a timeline that discovers files or rows it does not own must add MarkReadonly() before upgrading.
New in this area: IMarkArtifactsReadonly, MarkReadonly() on the artifact-declaring builder steps, ArtifactInstance.IsReadonly, and ArtifactMarkedReadonlyException when something tries to deconstruct a readonly artifact. A reference separately reports whether it *can* be deconstructed; MarkReadonly() answers whether it *may* be, and teardown needs a yes from both.
Also in 0.3.0, and the only thing its own notes described: debugger protocol v4 - enums travel as names, a step's plan states its policies, and an assertion carries typed arguments rather than rendered sentences.