Runic.CommandLine.Testing
0.9.0
See the version list below for details.
dotnet add package Runic.CommandLine.Testing --version 0.9.0
NuGet\Install-Package Runic.CommandLine.Testing -Version 0.9.0
<PackageReference Include="Runic.CommandLine.Testing" Version="0.9.0" />
<PackageVersion Include="Runic.CommandLine.Testing" Version="0.9.0" />
<PackageReference Include="Runic.CommandLine.Testing" />
paket add Runic.CommandLine.Testing --version 0.9.0
#r "nuget: Runic.CommandLine.Testing, 0.9.0"
#:package Runic.CommandLine.Testing@0.9.0
#addin nuget:?package=Runic.CommandLine.Testing&version=0.9.0
#tool nuget:?package=Runic.CommandLine.Testing&version=0.9.0
Runic.CommandLine.Testing
Use this package in tests for deterministic command invocation fixtures. It provides an in-memory console, a simple scope factory, parsed invocations, captured parse settings, an already-cancelled token, and strict JSON envelope reading without reading or mutating process-global state.
Test the whole application
var tester = new CommandAppTester(console => new CommandApp(catalog)
{
Console = console,
ParseSettings = ParseSettings.Default,
HandleTerminationSignals = false,
});
var result = await tester.RunAsync(["greet", "Ada", "--format=json"]);
CommandResponse<Greeting> response = CommandTestEnvelope.Read(result.StandardOutput, "hello.greeting/1", AppJsonContext.Default.Greeting);
For prompt tests, construct TestCommandConsole with explicit capability flags
and call QueueInput. Each tester run gets fresh output buffers.
Read the JSON envelope
CommandTestEnvelope.Read<T>(json, expectedPayloadType, typeInfo) reads one
captured runic.commandline/1 response and returns its CommandResponse<T>:
the typed Payload on success, or the Fault, FailureData and Diagnostics
on failure. It applies every check of CommandJsonEnvelopeReader.Read<T>: one
compact JSON object followed by exactly one LF, the size and depth limits, all
nine envelope members with their JSON types, no duplicate members, the success
and failure rules, and on success a declared payloadType equal to
expectedPayloadType (the identity of the command's [CommandResult] or
codec). A client of the protocol ignores members it does not know; a test
should not, so Read also rejects any member the protocol does not define on
the envelope, the fault, the fault's data or a diagnostic. Every failure is a
CommandProtocolException whose Kind names it, for example
missing-required-field, invalid-envelope, unsupported-payload-type (a
success with another payload type), payload-shape-mismatch or
unknown-member.
The payload is read with the supplied source-generated metadata only, so it is
safe under trimming and NativeAOT; its own unknown properties follow that
metadata (set UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow on
the context to reject them). A failure response has no payload type, so the
expected one is checked only on success:
CommandResponse<Greeting> response = CommandTestEnvelope.Read(result.StandardOutput, "hello.greeting/1", AppJsonContext.Default.Greeting);
if (!response.Succeeded || response.Payload!.Name != "Ada") throw new Exception("Unexpected greeting.");
CommandTestEnvelope.Parse checks the framing only and returns a JsonDocument
for ad-hoc inspection of any JSON value.
Capture an environment
CommandTestEnvironment.Capture builds the ParseSettings that CommandApp
captures from the process environment, from a dictionary instead: its
RUNIC_COMMANDLINE_OUTPUT entry selects the output mode, and
RUNIC_COMMANDLINE_NO_INPUT and option environment fallbacks read the same
entries. The process environment is never read, and later changes to the
dictionary do not affect the settings.
ParseSettings settings = CommandTestEnvironment.Capture(new Dictionary<string, string?>
{
["RUNIC_COMMANDLINE_OUTPUT"] = "json",
["RUNIC_COMMANDLINE_NO_INPUT"] = "1",
});
var tester = new CommandAppTester(console => new CommandApp(catalog)
{
Console = console, ParseSettings = settings, HandleTerminationSignals = false,
});
Capture() without variables is an empty environment.
Test a continuation
Run is the synchronous counterpart of RunAsync. It runs the application with
CommandApp.Run, so a command that hands a continuation back with
CommandExecutionContext.ContinueOnCallingThread has it run on the thread that
calls Run:
CommandAppTestResult result = tester.Run(["ui", "--no-open"]);
ExitCode is the continuation's result, or the invocation's exit code when it
failed or handed nothing back. StandardOutput and StandardError are captured
after the continuation returned. Call it from a test that does not hold a
SynchronizationContext the handler depends on; the invocation itself runs
without one.
Script prompt answers
CommandPrompts reads one line per prompt from an interactive
TestCommandConsole. Supply the console through CreateConsole:
var tester = new CommandAppTester(console => new CommandApp(catalog) { Console = console, ParseSettings = ParseSettings.Default })
{
CreateConsole = () =>
{
var console = new TestCommandConsole { IsInteractive = true };
console.QueueInput("2"); // the second choice of a select, or its label
console.QueueInput("y"); // a confirm
return console;
},
};
A queued null is the end of input, which cancels the command (exit code 130).
The default noninteractive console never asks: prompts return their defaults or
fail with RCLI4001. To drive the redrawn terminal prompts, set
SupportsAnsi = true (with SupportsUnicode and Width as needed) and queue
keys with QueueKey(ConsoleKey.DownArrow), QueueKey(ConsoleKey.Enter) or
QueueText("name\n"). Their frames, ANSI sequences included, are captured in
StandardError. ReadKeyAsync waits until a key is queued or the token is
cancelled, which lets a test cancel a waiting prompt. Keep a reference to the
console you create and check PendingInputCount and PendingKeyCount to make
sure every queued answer was used.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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
- Runic.CommandLine (= 0.9.0)
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 |
|---|---|---|
| 0.9.1 | 0 | 10/26/2026 |
| 0.9.0 | 135 | 10/22/2026 |
| 0.8.0 | 163 | 10/22/2026 |
| 0.7.0-preview.2 | 179 | 10/17/2026 |
| 0.7.0-preview.1 | 279 | 10/14/2026 |
| 0.6.0-preview.5 | 162 | 10/12/2026 |
| 0.6.0-preview.4 | 64 | 10/12/2026 |
| 0.6.0-preview.3 | 183 | 10/9/2026 |
| 0.6.0-preview.2 | 91 | 10/7/2026 |
| 0.6.0-preview.1 | 107 | 10/5/2026 |
| 0.5.0-preview.3 | 58 | 10/1/2026 |
| 0.5.0-preview.2 | 70 | 9/25/2026 |
| 0.4.0-preview.1 | 66 | 9/22/2026 |
| 0.3.0-preview.1 | 76 | 9/11/2026 |
| 0.2.0-preview.1 | 83 | 9/9/2026 |