-
Notifications
You must be signed in to change notification settings - Fork 13
Unit testing
Typin provides a convenient way to write functional tests for your applications, thanks to the IConsole
interface. While a command running in production uses SystemConsole
for console interactions (unless other is specified), you can rely on VirtualConsole
in your tests to validate these interactions in a simulated environment.
When you initialize an instance of VirtualConsole
, you can supply your own streams which will be used as the application's stdin, stdout, and stderr. You don't have to supply all of them, however, and any remaining streams will be substituted with a no-op stub.
To illustrate how to use this, let's look at an example. Assume you want to test a simple command such as this one:
[Command]
public class ConcatCommand : ICommand
{
[CommandOption("left")]
public string Left { get; set; } = "Hello";
[CommandOption("right")]
public string Right { get; set; } = "world";
public ValueTask ExecuteAsync(IConsole console)
{
console.Output.Write(Left);
console.Output.Write(' ');
console.Output.Write(Right);
return default;
}
}
[Test]
public async Task ConcatCommand_Test()
{
// Arrange
// a) Manual streams creation
//await using var stdOut = new MemoryStream();
//var console = new VirtualConsole(output: stdOut);
// b) Using CreateBuffered
var (console, stdOut, _) = VirtualConsole.CreateBuffered();
var app = new CliApplicationBuilder()
.AddCommand<ConcatCommand>()
.UseConsole(console)
.Build();
var args = new[] {"--left", "foo", "--right", "bar"};
var envVars = new Dictionary<string, string>();
// Act
await app.RunAsync(args, envVars);
//var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray());
// Assert
Assert.That(stdOut.GetString(), Is.EqualTo("foo bar"));
}
As a general recommendation, it's always more preferable to test at the application level. While you can validate your command's execution adequately simply by testing its ExecuteAsync()
method, testing end-to-end also helps you catch bugs related to configuration, such as incorrect option names, parameter order, fallback variable names, etc.
Additionally, it's important to remember that commands in CliFx are not constrained to text and can produce binary data. In such cases, you can still use the above setup but call GetBytes()
instead of GetString()
:
// Act
await app.RunAsync(args, envVars);
// Assert
Assert.That(stdOut.GetBytes(), Is.EqualTo(new byte[] {1, 2, 3, 4, 5}));
In some scenarios the binary data may be too large to load in-memory. If that's the case, it's recommended to use VirtualConsole
directly with custom streams.
Getting started
Advanced features
- Reporting errors
- Exception handling
- Metadata and startup message
- Graceful cancellation
- Dependency injection
- Middleware pipeline
- Environment variables
Utilities
Tests
Misc