diff --git a/examples/Cli/Testing/CommandAppTests.cs b/examples/Cli/Testing/CommandAppTests.cs
new file mode 100644
index 0000000..27612bb
--- /dev/null
+++ b/examples/Cli/Testing/CommandAppTests.cs
@@ -0,0 +1,47 @@
+using Spectre.Console;
+using Spectre.Console.Cli;
+using Spectre.Console.Testing;
+
+namespace Testing;
+
+[TestClass]
+public class CommandAppTests
+{
+ ///
+ /// 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.");
+ }
+}
\ No newline at end of file
diff --git a/examples/Cli/Testing/ConsoleTests.cs b/examples/Cli/Testing/ConsoleTests.cs
new file mode 100644
index 0000000..ec0cb54
--- /dev/null
+++ b/examples/Cli/Testing/ConsoleTests.cs
@@ -0,0 +1,43 @@
+using Spectre.Console;
+using Spectre.Console.Testing;
+
+namespace Testing;
+
+[TestClass]
+public class ConsoleTests
+{
+ [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 │
+└─────────────┘
+
+"""");
+ }
+
+ [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");
+ }
+}
\ No newline at end of file
diff --git a/examples/Cli/Testing/Testing.csproj b/examples/Cli/Testing/Testing.csproj
new file mode 100644
index 0000000..3ef1ad5
--- /dev/null
+++ b/examples/Cli/Testing/Testing.csproj
@@ -0,0 +1,30 @@
+
+
+
+ net8.0
+ enable
+ enable
+ false
+ true
+ Testing
+ Demonstrates how to unit test a Spectre.Console console application.
+ Cli
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/Examples.sln b/examples/Examples.sln
index 136e2a8..239bce8 100644
--- a/examples/Examples.sln
+++ b/examples/Examples.sln
@@ -75,9 +75,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Layout", "Console\Layout\La
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Json", "Console\Json\Json.csproj", "{ABE3E734-0756-4D5A-B28A-E6E526D9927D}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Help", "Cli\Help\Help.csproj", "{BAB490D6-FF8D-462B-B2B0-933384D629DB}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Help", "Cli\Help\Help.csproj", "{BAB490D6-FF8D-462B-B2B0-933384D629DB}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Decorations", "Console\Decorations\Decorations.csproj", "{FC5852F1-E01F-4DF7-9B49-CA19A9EE670F}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Decorations", "Console\Decorations\Decorations.csproj", "{FC5852F1-E01F-4DF7-9B49-CA19A9EE670F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testing", "Cli\Testing\Testing.csproj", "{3BBDD42E-C1B4-4FDB-B87A-1C16CB2241AF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -521,6 +523,18 @@ Global
{FC5852F1-E01F-4DF7-9B49-CA19A9EE670F}.Release|x64.Build.0 = Release|Any CPU
{FC5852F1-E01F-4DF7-9B49-CA19A9EE670F}.Release|x86.ActiveCfg = Release|Any CPU
{FC5852F1-E01F-4DF7-9B49-CA19A9EE670F}.Release|x86.Build.0 = Release|Any CPU
+ {3BBDD42E-C1B4-4FDB-B87A-1C16CB2241AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3BBDD42E-C1B4-4FDB-B87A-1C16CB2241AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3BBDD42E-C1B4-4FDB-B87A-1C16CB2241AF}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {3BBDD42E-C1B4-4FDB-B87A-1C16CB2241AF}.Debug|x64.Build.0 = Debug|Any CPU
+ {3BBDD42E-C1B4-4FDB-B87A-1C16CB2241AF}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {3BBDD42E-C1B4-4FDB-B87A-1C16CB2241AF}.Debug|x86.Build.0 = Debug|Any CPU
+ {3BBDD42E-C1B4-4FDB-B87A-1C16CB2241AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3BBDD42E-C1B4-4FDB-B87A-1C16CB2241AF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3BBDD42E-C1B4-4FDB-B87A-1C16CB2241AF}.Release|x64.ActiveCfg = Release|Any CPU
+ {3BBDD42E-C1B4-4FDB-B87A-1C16CB2241AF}.Release|x64.Build.0 = Release|Any CPU
+ {3BBDD42E-C1B4-4FDB-B87A-1C16CB2241AF}.Release|x86.ActiveCfg = Release|Any CPU
+ {3BBDD42E-C1B4-4FDB-B87A-1C16CB2241AF}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -533,6 +547,7 @@ Global
{37024E79-A857-4EB2-9B50-F724ED34E5EB} = {4682E9B7-B54C-419D-B92F-470DA4E5674C}
{DD8EC1B0-F50C-44E4-8399-2D560F95E572} = {2571F1BD-6556-4F96-B27B-B6190E1BF13A}
{BAB490D6-FF8D-462B-B2B0-933384D629DB} = {4682E9B7-B54C-419D-B92F-470DA4E5674C}
+ {3BBDD42E-C1B4-4FDB-B87A-1C16CB2241AF} = {4682E9B7-B54C-419D-B92F-470DA4E5674C}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3EE724C5-CAB4-410D-AC63-8D4260EF83ED}
diff --git a/examples/Shared/Shared.csproj b/examples/Shared/Shared.csproj
index a6dcbd2..d5cc440 100644
--- a/examples/Shared/Shared.csproj
+++ b/examples/Shared/Shared.csproj
@@ -11,6 +11,7 @@
+