如何在测试方法中模拟按键?
How to mock a keypress in test method?
我写了一个生命游戏控制台应用程序,现在我正在为它编写单元测试。游戏板以循环方式呈现,可以使用 Esc 键中断。但是我不知道如何在我的主应用程序测试方法中模拟该按键 class 所以我的测试当前无限循环。
Application.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);
}
ApplicationTests.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);
}
我写了一个生命游戏控制台应用程序,现在我正在为它编写单元测试。游戏板以循环方式呈现,可以使用 Esc 键中断。但是我不知道如何在我的主应用程序测试方法中模拟该按键 class 所以我的测试当前无限循环。
Application.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);
}
ApplicationTests.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);
}