0

I want to test a game loop that runs forever until the user quits or dies, after that a message is displayed.

Something like this:

    [TestFixtureSetUp]
    public void SetupMethods()
    {
        _input = new Mock<IInput>();
        _input.SetupProperty(i => i.QuitKey, 'q');
        _messenger = new Mock<IMessenger>();
        _grid = new Mock<IGrid>();
        _clock = new Mock<IClock>();
        _game = new Game(_input.Object, _messenger.Object, _grid.Object, _clock.Object);
    }

    [Test]
    public void should_loop_until_player_quits_then_show_confirmation()
    {
        _input.Setup(g => g.ReadKey()).Returns(_input.Object.QuitKey);

        _messenger.Verify(m => m.SendMessage("Are you sure?"));
    }

    [Test]
    public void should_loop_until_player_dies_then_show_game_over_message()
    {
        _game.IsGameOver = true;

        _messenger.Verify(m => m.SendMessage("Game Over"));
    }

inside the Game class I have

    public void GameLoop()
    {
        //gets player input
        while (ReadKey() != _input.QuitKey && !IsGameOver)
        {
            //waits for clock
            if (_clock.TimeElapsed % 1000 == 0)
            {
                //logic
            }               
        }

        if (IsGameOver)
        {
            _messenger.SendMessage("Game Over");
        }
        else
        {
            _messenger.SendMessage("Are you sure?");
        }
    }

to allow the loop to run I found that creating a separate thread for it works, and doesn't hang the test. However when I put the new Thread code in the Setup the tests randomly fail if I do Test All (I have about 5 or 6). If I do each test separately it's fine.

    [SetUp]
    public void SetupTest()
    {
        new Thread(() =>
        {
            Thread.CurrentThread.IsBackground = true;
            _game.GameLoop();
        }).Start();
    }

What am I doing wrong?

happygilmore
  • 3,008
  • 4
  • 23
  • 37
  • Why are you doing this in a test? "...until the user quits or dies..." - if you are wanting the user to run the game, make an application. – adrianbanks Dec 23 '13 at 01:44
  • not sure I understand the question, also I'm doing this really more as an exercise to practice TDD than any real application. I'm still quite new to it. – happygilmore Dec 23 '13 at 01:49
  • @adrianbanks look at the question http://stackoverflow.com/a/5717938/1821057 you will see what I was originally trying for. – happygilmore Dec 23 '13 at 06:36

1 Answers1

0

Your tests are inconclusive because they're

  • run in random order
  • in your tests, you setup various return values on your mocks

The consequence is: You will never exactly know what values are seen by your game loop.

To overcome this, you'll have to setup all your mocks anew on a per-test basis (i.e. in SetUp, not in TestFixtureSetUp), because, to my knowledge, Moq doesn't have something like a Clear() method for Mocks.

Edit
In addition, you must make sure that your variables are passed correctly to the thread. Try this:

[SetUp]
public void SetupTest()
{
    _input = new Mock<IInput>();
    _input.SetupProperty(i => i.QuitKey, 'q');
    _messenger = new Mock<IMessenger>();
    _grid = new Mock<IGrid>();
    _clock = new Mock<IClock>();

    new Thread(() =>
    {
        Thread.CurrentThread.IsBackground = true;

        IInput input = _input.Object;
        IMessenger messenger = _messenger.Object;
        IGrid grid = _grid.Object;
        IClock clock = _clock.Object;

        _game = new Game(input, messenger, grid, clock);
        _game.GameLoop();
    }).Start();
}
Thomas Weller
  • 11,631
  • 3
  • 26
  • 34