c# - 如何在测试方法中模拟按键?
问题描述
我写了一个生活游戏控制台应用程序,现在我正在为它编写单元测试。游戏板呈现在一个循环中,可以用 Esc 键打破。但是,我不知道如何在我的主应用程序类的测试方法中模拟该按键,因此我的测试当前无限循环。
应用程序.cs
public class Application
{
private readonly IGame _game;
private readonly IBoard _board;
private readonly IBoardGenerator _boardGenerator;
private readonly IConsole _console;
public Application(IGame game, IBoard board, IBoardGenerator boardGenerator, IConsole console)
{
_game = game;
_board = board;
_boardGenerator = boardGenerator;
_console = console;
}
public void Run()
{
void RenderBoard()
{
_console.Clear();
_board.Evolve();
_board.Print();
Thread.Sleep(150);
}
LoopUntilButtonPressed(() =>
{
_console.Clear();
_game.NewGame();
_game.SetBoard(_board, _boardGenerator);
LoopUntilButtonPressed(RenderBoard, ConsoleKey.Escape);
}, ConsoleKey.Escape);
}
private void LoopUntilButtonPressed(Action action, ConsoleKey consoleKey)
{
do
{
while (!_console.KeyAvailable)
{
action.Invoke();
}
} while (_console.ReadKey(true) != consoleKey);
}
应用程序测试.cs
[TestFixture]
public class ApplicationTests
{
private Mock<IGame> _fakeGame;
private Mock<IBoard> _fakeBoard;
private Mock<IBoardGenerator> _fakeBoardGenerator;
private Mock<IConsole> _fakeConsole;
private Application _application;
[SetUp]
public void SetUp()
{
_fakeGame = new Mock<IGame>();
_fakeBoard = new Mock<IBoard>();
_fakeBoardGenerator = new Mock<IBoardGenerator>();
_fakeConsole = new Mock<IConsole>();
_application = new Application(_fakeGame.Object, _fakeBoard.Object, _fakeBoardGenerator.Object, _fakeConsole.Object);
}
[Test]
public void Run_MethodCalled_GameCorrectlySet()
{
_application.Run();
_fakeConsole.Setup(c => c.ReadKey(It.IsAny<bool>())).Returns(ConsoleKey.Escape);
_fakeConsole.Setup(c => c.KeyAvailable).Returns(true);
_fakeGame.Verify(g => g.NewGame(), Times.Once);
}
}
解决方案
在控制台抽象上模拟ReadKey
和成员。KeyAvailable
确保Setup
在被测方法之前发生。在这种情况下是Run
. 这样,模拟在调用时将按预期运行。
我还建议您设置一个序列,KeyAvailable
以便在while
.
[Test]
public void Run_MethodCalled_GameCorrectlySet() {
//Arrange
_fakeConsole.Setup(_ => _.ReadKey(It.IsAny<bool>())).Returns(ConsoleKey.Escape);
_fakeConsole.SetupSequence(_ => _.KeyAvailable)
.Returns(false) // will be returned on 1st invocation
.Returns(true); // will be returned on 2nd invocation to break while
//Act
_application.Run();
//Assert
_fakeGame.Verify(_ => _.NewGame(), Times.Once);
}
推荐阅读
- css - 响应式 CSS 网格
- swift - Swift String 到 Int 总是返回 nil
- sas - Proc SQL 运行总计
- java - 使用 @Value 注释的属性始终为 null
- cygwin - Cygwin 文件夹太大,占用 10gb?有没有办法在不重新安装的情况下减少这种情况?
- python - 当通过管道传递大型数组时,Python 多处理卡住了
- groovy - 使用 groovy 脚本复制 xml 命名空间节点
- c# - 单击关闭面板时触发两次功能
- azure - 如何在最新的 azure webjob 3.03 中指定 AzureWebJobsStorage
- bash - 日期输出作为命令行参数