Mocking User Input With a Queue

Over the course of my Tic Tac Toe project, I’ve needed to test against user input several times. As a newcomer to mock object patterns, coming up with good solutions to these testing dilemmas has been one of my biggest challenges.

There are plenty of heavy-duty tools for mocks and fakes in Java, but I’d like to stick with my own solutions as long as possible, since writing them myself has been enlightening.

Here’s a solution I came up with today to simulate full Tic Tac Toe games with two human players: a mock View object that returns fake input from a queue. By pushing mock input onto the queue during test setup, I can configure games in advance and replay them later. Here’s the very simple mock View object:

MockTerminalView.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.util.NoSuchElementException;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;

public class MockTerminalView extends TerminalView {

    private Queue<String> inputQ = new LinkedBlockingQueue<String>();

    public void enqueueInput(String fakeInput) {
        inputQ.add(fakeInput);
    }

    public void clearInput() {
        inputQ.clear();
    }

    @Override
    public String getInput() {
        try {
            return inputQ.remove();
        } catch (NoSuchElementException e) {
            return "";
        }
    }
}

And here’s an example test that plays through an entire game and exits when Player 2 wins (comments added for some context):

GameControllerTest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Test
public void gameShouldEndOnWin() {
    exit.expectSystemExit();

    controller.newGame();

    // Select two human players
    view.enqueueInput("h");
    view.enqueueInput("h");

    controller.setUp();


    view.enqueueInput("middle center"); // Player 1's first move
    view.enqueueInput("top left");      // Player 2's first move
    view.enqueueInput("top right");     // Player 1 goes for the diagonal...
    view.enqueueInput("middle left");   // Player 2 goes for the column...
    view.enqueueInput("lower right");   // Player 1 chokes!
    view.enqueueInput("lower left");    // Player 2 wins! What an upset! 

    controller.startGame();
}

I’m sure I’ll discover the shortcomings of this approach sooner or later, but for now it’s a pretty good way to test events inside the main game loop.

Comments