An automated test fixture for .NET console applications
Using ConsoleScenario, you can run a program, define a list of expectations and input so that your tests verify both the behavior and the output.
Scenarios.Create("myapp.exe", "-argument")
.Expect("Welcome to my app!")
.ExpectPrompt("Do you want to continue? (y/n)")
.Input("y")
.Expect("You selected 'yes'")
.Run(),
These are all the expectations and console interactions supported by ConsoleScenario. You can also create your own assertions and steps.
Most functions also support a TimeSpan
parameter to define a timeout, which will kill the process.
Most expectations will fail if the error stream contains something.
.Expect("This line should be present")
insures that the next line is exactly this.Expect("Multiple", "Lines", "At", "Once")
does the same thing as several back-to-backExpect
calls.Expect(line => line.Contains("something"))
allows providing a callback, receiving the line and returning whether the assertion is true.Any(5)
ignores the line content, as long as the console indeed returns a line
.ExpectPrompt("Enter your name:")
is similar toExpect
, but won't wait for the line break.Input("John Doe")
sends the characters to the console
.ExpectNothingElse()
will fail if the console outputs more lines.IgnoreRemaining()
will ignore everything the console outputs until it closes.ExpectExitCode(-1)
will fail if the console exit code is not the provided one.IgnoreExitCode()
will not verify the exit code
.ExpectError("Input string invalid")
will pass if the error stream contains that string
.Until(line => line.Contains("100%"))
will check every line until the callback returns true
.Extract("Job ID: (.+)", values => jobId = values[0])
is a shortcut to run a regex and provide the values in the callback.Expect(() => "Starting job " + jobId)
is the same asExpect
but allows resolving the string at runtime, allowing the usage ofExtract
variables
The general principle is that the console Input
, Output
and Error
streams are being managed by the ProcessRuntime
object, which is created and inject in Scenario
by the Scenarios
class for you.
The Scenario
class simply executes IScenarioStep
instances one by one until none are left.
The Scenario
class is the starting point to running assertions.
You can set the ExpectedExitCode
property to null to ignore the exit code, or to your expected exit code.
Add steps using AddStep(step)
and/or AddSteps(steps)
.
Execute Run()
when you are ready to run all steps.
ReadLineAssertionStep
will read a line, run it against anIAssertion
and repeat if specified. Most expectations use this.ReadUntilStep
will read lines until the specified condition is trueInputStep
will write the specified line in theInput
streamReadCharsStep
will read characters until the provided string has been reachedReadErrorLineAssertionStep
is similar toReadLineAssertionStep
, but checks in theError
stream
You can implement your own IAssertion
, and add it to the Scenario
steps.
Once you have implemented your assertion, call scenario.AddStep(new ReadLineAssertion(myAssertion))
where myAssertion
is your assertion instance.
You can return either AssertionResult.Pass()
or AssertionResult.Fail("Message", "The expected value")
For the fluent API, you can create your own ScenarioExtensions
class, like this:
public static class ScenarioExtensions
{
public static IScenario Input(this IScenario scenario)
{
scenario.AddStep(new ReadLineAssertionStep(new MyAssertion()));
return scenario;
}
}
If you need very advanced control of the flow of the assertion, you can implement your own IScenarioStep
.
You have one function to implement, void Run(IAsyncDuplexStreamHandler asyncDuplexStreamHandler, ref int lineIndex);
.
In your implementation, you should read from asyncDuplexStreamHandler
as necessary, and increment lineIndex
every time a line is read.
If your assertion fails, you can throw a new ScenarioAssertionException
.
Copyright (c) 2015 Christian Rondeau, MIT License