From 1dabf25e1c912d2ab0976fb23e4db655d92717c6 Mon Sep 17 00:00:00 2001 From: Frank Ray <52075808+FrankRay78@users.noreply.github.com> Date: Tue, 8 Apr 2025 15:58:25 +0100 Subject: [PATCH] Add testing documentation (#1631) --- docs/Program.cs | 1 + docs/input/best-practices.md | 6 +- ...023-11-22-spectre-console-0.48-released.md | 8 +- docs/input/cli/unit-testing.md | 115 ++++++++++++++++++ docs/input/index.md | 14 ++- docs/src/Pipelines/CodePipeline.cs | 3 +- 6 files changed, 132 insertions(+), 15 deletions(-) create mode 100644 docs/input/cli/unit-testing.md diff --git a/docs/Program.cs b/docs/Program.cs index 24830bd..c1e4518 100644 --- a/docs/Program.cs +++ b/docs/Program.cs @@ -23,6 +23,7 @@ namespace Docs { "../../src/Spectre.Console/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs", "../../src/Spectre.Console.Cli/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs", + "../../src/Spectre.Console.Testing/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs", "../../src/Extensions/Spectre.Console.ImageSharp/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs", "../../src/Extensions/Spectre.Console.Json/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs" }) diff --git a/docs/input/best-practices.md b/docs/input/best-practices.md index dee4c2d..67f272c 100644 --- a/docs/input/best-practices.md +++ b/docs/input/best-practices.md @@ -85,10 +85,8 @@ on the main thread. ### Unit Testing Best Practices For testing of console output, Spectre.Console has [`IAnsiConsole`](xref:T:Spectre.Console.IAnsiConsole) that can be -injected into your application. -The [Spectre.Console.Test](https://www.nuget.org/packages/Spectre.Console.Testing/) contains a set of utilities for -capturing the output for verification, either manually or via a tool such -as [Verify](https://github.com/VerifyTests/Verify). +injected into your application. The [Spectre.Console.Test](https://www.nuget.org/packages/Spectre.Console.Testing/) +NuGet package contains utilities for capturing the console output for verification. See the [Unit Testing](cli/unit-testing) page for further guidance. ### Analyzer for Best Practices diff --git a/docs/input/blog/posts/2023-11-22-spectre-console-0.48-released.md b/docs/input/blog/posts/2023-11-22-spectre-console-0.48-released.md index 0c5cbd9..617a471 100644 --- a/docs/input/blog/posts/2023-11-22-spectre-console-0.48-released.md +++ b/docs/input/blog/posts/2023-11-22-spectre-console-0.48-released.md @@ -26,9 +26,9 @@ New features have been added, such as the ability to show separators between tab ## Rendering * Add .NET 8 support by [@patriksvensson](https://github.com/patriksvensson) in [#1367](https://github.com/spectreconsole/spectre.console/pull/1367) -* Fixed render issue where writeline inside status caused corrupt output #415 #694 by [@fredrikbentzen](https://github.com/fredrikbentzen) in [#1132](https://github.com/spectreconsole/spectre.console/pull/1132)) -* Relax the SDK requirements by rolling forward to the latest feature by [@0xced](https://github.com/0xced) in [#1237](https://github.com/spectreconsole/spectre.console/pull/1237)) -* Add fix to avoid exception on rows with no children by [@jeppevammenkristensen](https://github.com/jeppevammenkristensen) in [#1241](https://github.com/spectreconsole/spectre.console/pull/1241)) +* Fixed render issue where writeline inside status caused corrupt output #415 #694 by [@fredrikbentzen](https://github.com/fredrikbentzen) in [#1132](https://github.com/spectreconsole/spectre.console/pull/1132) +* Relax the SDK requirements by rolling forward to the latest feature by [@0xced](https://github.com/0xced) in [#1237](https://github.com/spectreconsole/spectre.console/pull/1237) +* Add fix to avoid exception on rows with no children by [@jeppevammenkristensen](https://github.com/jeppevammenkristensen) in [#1241](https://github.com/spectreconsole/spectre.console/pull/1241) * Set `end_of_line` to `LF` instead of `CRLF` by [@0xced](https://github.com/0xced) in [#1256](https://github.com/spectreconsole/spectre.console/pull/1256) * Fix `Rule` widget docs by [@tomaszprasolek](https://github.com/tomaszprasolek) in [#1257](https://github.com/spectreconsole/spectre.console/pull/1257) * Added the missing columns-cast by [@nils](https://github.com/nils)-a in [#1294](https://github.com/spectreconsole/spectre.console/pull/1294) @@ -46,7 +46,7 @@ New features have been added, such as the ability to show separators between tab ## CLI * Add async command unit tests by [@FrankRay78](https://github.com/FrankRay78) in [#1228](https://github.com/spectreconsole/spectre.console/pull/1228) -* Add support for async delegate by [@icalvo](https://github.com/icalvo) in [#1215](https://github.com/spectreconsole/spectre.console/pull/1215)) +* Add support for async delegate by [@icalvo](https://github.com/icalvo) in [#1215](https://github.com/spectreconsole/spectre.console/pull/1215) * Remove unnecessary `[NotNull]` attributes by [@0xced](https://github.com/0xced) in [#1255](https://github.com/spectreconsole/spectre.console/pull/1255) * Allow custom help providers by [@FrankRay78](https://github.com/FrankRay78) in [#1259](https://github.com/spectreconsole/spectre.console/pull/1259) * Specified details for settings for the argument vector by [@nils](https://github.com/nils)-a in [#1301](https://github.com/spectreconsole/spectre.console/pull/1301) diff --git a/docs/input/cli/unit-testing.md b/docs/input/cli/unit-testing.md new file mode 100644 index 0000000..05c940e --- /dev/null +++ b/docs/input/cli/unit-testing.md @@ -0,0 +1,115 @@ +Title: Unit Testing +Order: 14 +Description: Instructions for unit testing a Spectre.Console application. +Reference: + - T:Spectre.Console.Testing.CommandAppTester + - T:Spectre.Console.Testing.TestConsole + - T:Spectre.Console.Testing.TestConsoleInput +--- + +`Spectre.Console` 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.Testing` NuGet package. + +```text +> dotnet add package Spectre.Console.Testing +``` + +`Spectre.Console.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) + { + _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."); + } +``` + +## Testing console behaviour + + `TestConsole` and `TestConsoleInput` are testable implementations of `IAnsiConsole` and `IAnsiConsoleInput`, allowing you fine-grain control over testing console output and interactivity. + +The following example renders some widgets before then validating the console output: + +```csharp + [TestMethod] + public void Should_Render_Panel() + { + // Given + var console = new TestConsole(); + + // When + console.Write(new Panel(new Text("Hello World"))); + + // Then + Assert.AreEqual(console.Output, """" +┌─────────────┐ +│ Hello World │ +└─────────────┘ + +""""); + } +``` + +While `Assert` is fine for validating simple output, more complex output may benefit from a tool like [Verify](https://github.com/VerifyTests/Verify). + +The following example prompts the user for input before then validating the expected choice was made: + +```csharp + [TestMethod] + public void Should_Select_Orange() + { + // Given + var console = new TestConsole(); + console.Input.PushTextWithEnter("Orange"); + + // When + console.Prompt( + new TextPrompt("Favorite fruit?") + .AddChoice("Banana") + .AddChoice("Orange")); + + // Then + Assert.AreEqual(console.Output, "Favorite fruit? [Banana/Orange]: Orange\n"); + } +``` + +`CommandAppTester` uses `TestConsole` internally, which in turn uses `TestConsoleInput`, offering a fully testable harness for `Spectre.Console` widgets, prompts and commands. \ No newline at end of file diff --git a/docs/input/index.md b/docs/input/index.md index 8490224..4672cbd 100644 --- a/docs/input/index.md +++ b/docs/input/index.md @@ -6,7 +6,7 @@ Order: 0 Spectre.Console is a `.NET` library that makes it easier to create beautiful console applications. -## Spectre.Console.AnsiConsole Features +## Spectre.Console.AnsiConsole * Easily output text with different colors and even styles such as bold, italic and blinking with a Rich inspired [markup language](markup). * Supports `3`/`4`/`8`/`24`-bit colors in the terminal with auto-detection of the current terminal's capabilities. @@ -14,16 +14,19 @@ to create beautiful console applications. * Display progress for long running tasks with live displays of [progress](live/progress) and [status](live/status) controls. * Prompt user input with strongly typed [text input](prompts/text) or via [single-item select](prompts/selection) and [multiple item select](prompts/multiselection) controls. * Format .NET [exceptions](exceptions) with custom color coded themes and styles. -* Written with unit testing in mind. -Spectre.Console.AnsiConsole has been heavily inspired -by the excellent [Rich](https://github.com/willmcgugan/rich) library -for Python written by Will McGugan. +Spectre.Console.AnsiConsole has been heavily inspired by the excellent [Rich](https://github.com/willmcgugan/rich) library for Python written by Will McGugan. ## Spectre.Console.Cli * Create strongly typed settings and commands for parsing `args[]` to create complex command line applications like `git`, `gh`, or `dotnet` +## Spectre.Console.Testing + +* Spectre.Console has been developed with unit testing in mind. The Spectre.Console library itself is covered by an extensive test suite, project maintainers require test coverage for all new commits, and the same extension points and test harnesses used internally for testing are available to you. + +* The [Unit Testing](cli/unit-testing) page provides instructions for testing a Spectre.Console application. + ## Examples ![Sample of Spectre.Console output](./assets/images/example.png) @@ -36,3 +39,4 @@ for Python written by Will McGugan. Sorry, your browser doesn't support embedded videos. +The Spectre.Console [examples repository](https://github.com/spectreconsole/examples) contains many other examples. \ No newline at end of file diff --git a/docs/src/Pipelines/CodePipeline.cs b/docs/src/Pipelines/CodePipeline.cs index d505080..b3cd930 100644 --- a/docs/src/Pipelines/CodePipeline.cs +++ b/docs/src/Pipelines/CodePipeline.cs @@ -99,8 +99,7 @@ public class Api : Pipeline new ConcatDocuments(nameof(Code)), new CacheDocuments( new AnalyzeCSharp() - .WhereNamespaces(ns => ns.StartsWith("Spectre.Console") && !ns.Contains("Analyzer") && - !ns.Contains("Testing") && !ns.Contains("Examples")) + .WhereNamespaces(ns => ns.StartsWith("Spectre.Console") && !ns.Contains("Analyzer") && !ns.Contains("Examples")) .WherePublic(true) .WithCssClasses("code", "cs") .WithDestinationPrefix("api")