我做了一些调整,以防有人感兴趣。它处理混合大小写和其他控制台输出,例如提示和换行符。
提示密码:
internal static SecureString PromptForPassword(IConsoleWrapper console)
{
console.WriteLine("Enter password: ");
var pwd = new SecureString();
while (true)
{
ConsoleKeyInfo i = console.ReadKey(true);
if (i.Key == ConsoleKey.Enter)
{
break;
}
else if (i.Key == ConsoleKey.Backspace)
{
if (pwd.Length > 0)
{
pwd.RemoveAt(pwd.Length - 1);
console.Write("\b \b");
}
}
else if (i.KeyChar != '\u0000') // KeyChar == '\u0000' if the key pressed does not correspond to a printable character, e.g. F1, Pause-Break, etc
{
pwd.AppendChar(i.KeyChar);
console.Write("*");
}
}
console.WriteLine();
return pwd;
}
界面:
public interface IConsoleWrapper
{
void WriteLine(string value);
ConsoleKeyInfo ReadKey(bool intercept);
void Write(string value);
void WriteLine();
string ReadLine();
}
模拟控制台存根:
public class MockConsoleStub : IConsoleWrapper
{
private readonly IList<ConsoleKeyInfo> ckiCollection;
private int keyIndex = 0;
public MockConsoleStub(IList<ConsoleKeyInfo> mockKeyInfoCollection)
{
ckiCollection = mockKeyInfoCollection;
}
public readonly StringBuilder Output = new StringBuilder();
public ConsoleKeyInfo ReadKey()
{
var cki = ckiCollection[this.keyIndex];
keyIndex++;
return cki;
}
public void Write(string data)
{
Output.Append(data);
}
public void WriteLine(string value)
{
Output.AppendLine(value);
}
public void WriteLine()
{
Output.AppendLine();
}
public ConsoleKeyInfo ReadKey(bool intercept)
{
var cki = ckiCollection[this.keyIndex];
keyIndex++;
return cki;
}
public string ReadLine()
{
var sb = new StringBuilder();
var cki = ckiCollection[this.keyIndex];
keyIndex++;
while (cki.Key != ConsoleKey.Enter)
{
sb.Append(cki.KeyChar);
cki = ckiCollection[keyIndex];
keyIndex++;
}
return sb.ToString();
}
}
用法:
[TestMethod]
public void PromptForUsername_stub_password_GetsPassword()
{
var stub = new MockConsoleStub(new List<ConsoleKeyInfo>
{
new ConsoleKeyInfo('P', ConsoleKey.P, true, false, false),
new ConsoleKeyInfo('@', ConsoleKey.Attention, true, false, false),
new ConsoleKeyInfo('s', ConsoleKey.S, false, false, false),
new ConsoleKeyInfo('s', ConsoleKey.S, false, false, false),
new ConsoleKeyInfo('w', ConsoleKey.W, false, false, false),
new ConsoleKeyInfo('o', ConsoleKey.O, false, false, false),
new ConsoleKeyInfo('r', ConsoleKey.R, false, false, false),
new ConsoleKeyInfo('d', ConsoleKey.D, false, false, false),
new ConsoleKeyInfo('!', ConsoleKey.D1, true, false, false),
new ConsoleKeyInfo('\u0000', ConsoleKey.Enter, false, false, false),
});
var password = Settings.PromptForPassword(stub);
Assert.AreEqual("P@ssword!", SecureStringWrapper.ToString(password));
Assert.AreEqual($"Enter password: {Environment.NewLine}*********{Environment.NewLine}", stub.Output.ToString());
}
注意: SecureStringWrapper 返回字节数组或字符串。为了测试,我返回一个字符串。