我编写了一个生活控制台应用程序游戏,现在我正在为其编写单元测试。游戏板呈现为一个循环,可以通过Esc按下来断开。但是我不知道如何在主应用程序类的测试方法中模拟该按键,因此我的测试当前无限期地循环。
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);
}
}
答案 0 :(得分:1)
在控制台抽象上模拟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);
}