【问题标题】:How to mock a keypress in test method?如何在测试方法中模拟按键?
【发布时间】:2019-04-19 13:34:29
【问题描述】:

我编写了一个生活游戏控制台应用程序,现在我正在为它编写单元测试。游戏板呈现在一个循环中,可以用 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,有条件while (_console.ReadKey(true) != ConsoleKey.Escape);
  • @RomanDoskoch 在while(true) {...}
  • @Nkosi,是的,你是对的

标签: c# .net unit-testing nunit moq


【解决方案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);            
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-28
    • 1970-01-01
    • 2022-09-28
    • 1970-01-01
    • 2016-07-20
    • 1970-01-01
    相关资源
    最近更新 更多