I'm trying to write a unit test for a method like the one shown in the answer here: https://stackoverflow.com/a/3404522/824897
I created streams for input and output that I attached to the Console so it would be receiving input from my code instead of the keyboard and putting output to a stream instead of the normal standard out. This works for other read methods I had to create wrappers for, but not the one for reading a secret piece of information like a password. The method in the link above uses the ReadKey method with interrupt set to true, so we can stop the output from showing the character and put our own asterisk there instead (or just nothing while still capturing the key/character).
The problem for the unit test is that the ReadKey method seems to only react to actual key presses... not a string transformed into a stream passed in as the input. So, the unit test just hangs forever waiting for key presses.
Is there another way to write the method without using something that only responds to actual key presses? Or maybe a way to emulate key presses in the unit test? I fiddled with SendKey a little but couldn't get the Forms library necessary into the unit test project.
The method...
public string ReadLineMasked()
{
Console.Write(Prompt); // Prompt is a property of the class
ConsoleKey pressedKey;
var userInput = new StringBuilder();
do
{
var keyInfo = Console.ReadKey(true);
pressedKey = keyInfo.Key;
if (pressedKey == ConsoleKey.Backspace && userInput.Length > 0)
{
Console.Write("\b \b");
userInput.Length--;
}
else if (!char.IsControl(keyInfo.KeyChar))
{
Console.Write(MaskChar);
userInput.Append(keyInfo.KeyChar);
}
} while (pressedKey != ConsoleKey.Enter);
Console.WriteLine();
return userInput.ToString();
}
the unit test for the normal readline... that doesn't work if I use the masked readline...
public void ReadLineTests(string UserInput)
{
using var WriteStream = StreamHelper.GetWriterStream();
using var ReadSource = StreamHelper.GetReaderStream(UserInput);
Console.SetOut(WriteStream); // takes over the output that would normally show up in the console
Console.SetIn(ReadSource); // takes over as the input source so we can just send a bit of text to our method that needs testing
var sut = new SystemConsoleReader();
var inputResult = sut.ReadLine();
var writtenResult = StreamHelper.GetStringFromStreamWriter(WriteStream);
// At this point, the ReadLine we're testing should have written the command prompt to
// the WriteStream and stored the string from the ReadSource in inputResult. So, we
// need to test that the method wrote the expected command prompt to our output and
// we need to test that the input we got matches what we set.
Assert.Equal("TCLI> ", writtenResult);
Assert.Equal(UserInput, inputResult);
}
And the little helper method I wrote to pull a string value from the writer stream...
public static string GetStringFromStreamWriter(StreamWriter writer)
{
writer.Flush(); // you can't read from the StreamWriter until the Flush happens. So... Do this.
writer.BaseStream.Position = 0; // current position is probably the end of the data, so restart it.
using var reader = new StreamReader(writer.BaseStream);
var recoveredValue = reader.ReadToEnd();
writer.Close();
writer.Dispose();
return recoveredValue;
}