如何在测试方法中模拟按键?

3
我写了一个生命游戏控制台应用程序,现在我正在为它编写单元测试。游戏棋盘是在一个循环中渲染的,可以通过按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);            
    }
}

只需在控制台抽象中模拟 ReadKeyKeyAvailable 成员即可。 - Nkosi
@Nkosi,这里有一个条件 while (_console.ReadKey(true) != ConsoleKey.Escape); - Roman
@RomanDoskoch,这是在 while(true) {...} 循环内部的内容。 - Nkosi
@Nkosi,是的,你说得对。 - Roman
@KarolSkrobot 在测试方法之前需要进行设置。你的编辑显示在调用“Run”之后。 - Nkosi
显示剩余3条评论
1个回答

1

在控制台抽象层中模拟ReadKeyKeyAvailable成员。

确保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);            
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接