Title: Unit Testing Order: 14 Description: Instructions for unit testing a Spectre.Console.Cli application. --- `Spectre.Console.Cli` has a separate project that contains test harnesses for unit testing your own console applications. The fastest way of getting started is to install the `Spectre.Console.Cli.Testing` NuGet package. ```text > dotnet add package Spectre.Console.Cli.Testing --prerelease ``` `Spectre.Console.Cli.Testing` is also the namespace containing the test classes. ## Testing a CommandApp The `CommandAppTester` is a test implementation of `CommandApp` that's configured in a similar manner but designed for unit testing. The following example validates the exit code and terminal output of a `Spectre.Console` command: ```csharp /// /// A Spectre.Console Command /// public class HelloWorldCommand : Command { private readonly IAnsiConsole _console; public HelloWorldCommand(IAnsiConsole console) { // nb. AnsiConsole should not be called directly by the command // since this doesn't play well with testing. Instead, // the command should inject a IAnsiConsole and use that. _console = console; } public override int Execute(CommandContext context, CancellationToken cancellationToken) { _console.WriteLine("Hello world."); return 0; } } [TestMethod] public void Should_Output_Hello_World() { // Given var app = new CommandAppTester(); app.SetDefaultCommand(); // When var result = app.Run(); // Then Assert.AreEqual(result.ExitCode, 0); Assert.AreEqual(result.Output, "Hello world."); } ``` The following example demonstrates how to mock user inputs for an interactive command. This test (InteractiveCommand_WithMockedUserInputs_ProducesExpectedOutput) simulates user interactions by pushing predefined inputs to the console, then verifies that the resulting output is as expected. ```csharp public sealed class InteractiveCommandTests { private sealed class InteractiveCommand : Command { private readonly IAnsiConsole _console; public InteractiveCommand(IAnsiConsole console) { _console = console; } public override int Execute(CommandContext context, CancellationToken cancellationToken) { var fruits = _console.Prompt( new MultiSelectionPrompt() .Title("What are your [green]favorite fruits[/]?") .NotRequired() // Not required to have a favorite fruit .PageSize(10) .MoreChoicesText("[grey](Move up and down to reveal more fruits)[/]") .InstructionsText( "[grey](Press [blue][/] to toggle a fruit, " + "[green][/] to accept)[/]") .AddChoices(new[] { "Apple", "Apricot", "Avocado", "Banana", "Blackcurrant", "Blueberry", "Cherry", "Cloudberry", "Coconut", })); var fruit = _console.Prompt( new SelectionPrompt() .Title("What's your [green]favorite fruit[/]?") .PageSize(10) .MoreChoicesText("[grey](Move up and down to reveal more fruits)[/]") .AddChoices(new[] { "Apple", "Apricot", "Avocado", "Banana", "Blackcurrant", "Blueberry", "Cherry", "Cloudberry", "Cocunut", })); var name = _console.Ask("What's your name?"); _console.WriteLine($"[{string.Join(',', fruits)};{fruit};{name}]"); return 0; } } [Fact] public void InteractiveCommand_WithMockedUserInputs_ProducesExpectedOutput() { // Given TestConsole console = new(); console.Interactive(); // Your mocked inputs must always end with "Enter" for each prompt! // Multi selection prompt: Choose first option console.Input.PushKey(ConsoleKey.Spacebar); console.Input.PushKey(ConsoleKey.Enter); // Selection prompt: Choose second option console.Input.PushKey(ConsoleKey.DownArrow); console.Input.PushKey(ConsoleKey.Enter); // Ask text prompt: Enter name console.Input.PushTextWithEnter("Spectre Console"); var app = new CommandAppTester(null, new CommandAppTesterSettings(), console); app.SetDefaultCommand(); // When var result = app.Run(); // Then result.ExitCode.ShouldBe(0); result.Output.EndsWith("[Apple;Apricot;Spectre Console]"); } } ```