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-backExpectcalls.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 asExpectbut allows resolving the string at runtime, allowing the usage ofExtractvariables
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.
ReadLineAssertionStepwill read a line, run it against anIAssertionand repeat if specified. Most expectations use this.ReadUntilStepwill read lines until the specified condition is trueInputStepwill write the specified line in theInputstreamReadCharsStepwill read characters until the provided string has been reachedReadErrorLineAssertionStepis similar toReadLineAssertionStep, but checks in theErrorstream
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