Как смоделировать нажатие клавиши в тестовом методе? - PullRequest
2 голосов
/ 19 апреля 2019

Я написал игровое консольное приложение life и сейчас пишу для него юнит-тесты. Игровое поле отображается в виде петли, которая может быть разорвана нажатием 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);            
    }
}

1 Ответ

1 голос
/ 19 апреля 2019

Макетируйте элементы 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);            
}
...