Currently I'm reading the great book "Growing Object-Oriented Software, Guided by Tests" by Steve Freeman and Nat Pryce. They encourage us to drive software development in the large with an outer loop of end-to-end acceptance tests and in the small with an inner loop of unit tests. While implementing just for fun the
Nine Men's Morris board game with a simple console user interface, I tried to get a feeling for "test guided growing of software" as it is described in the book.
During that exercise I needed to find a solution to control the input towards and the output from the console. The following example source code shows one possible solution.
I started with an acceptance test. I choose to implement it with JUnit, but
Fitnesse tool would have been an alternative.
public class NineMensMorrisAcceptanceTests {
private ApplicationRunner application = new ApplicationRunner();
@Test
public void applicationAsksForUserMoveAndThenMakesOwnMove()
{
application.startGame();
application.hasDisplayed("Nine Men's Morris");
application.hasDisplayed("Please enter spot to place piece:");
application.userEnters("1\r\n");
application.hasDisplayed("Computer places piece on spot: 2");
}
}
The ApplicationRunner class starts the console application in a new thread and acquires control over the input and output streams. Luckily Java has such a well designed IO system, which allows easy test set up. The game application writes to the console via
System.out and reads from the console via
System.in:
public class ApplicationRunner {
private PipedOutputStream pipedOutputStream;
private PipedInputStream pipedInputStream;
private ByteArrayOutputStream outputStream;
public ApplicationRunner(){
pipedOutputStream = new PipedOutputStream();
pipedInputStream = new PipedInputStream(pipedOutputStream);
System.setIn(pipedInputStream);
outputStream = new ByteArrayOutputStream();
System.setOut(new PrintStream(outputStream));
}
public void startGame() {
Thread thread = new Thread("Test Application"){
@Override public void run(){Console.main(null);}
};
thread.setDaemon(true);
thread.start();
}
public void hasDisplayed(String text) {
boolean displayed = false; int tries = 20;
while(tries>0 && !displayed){
Thread.sleep(100);
displayed = outputStream.toString().contains(text) ? true : false;
tries--;
}
if (!displayed){
throw new AssertionError("Missing text in output: " + text);
}
}
public void userEnters(String userInput) {
pipedOutputStream.write(userInput.getBytes());
}
}
The
Console.main() method set ups and starts the console application:
public static void main(String[] args) {
ConsoleGameUI consoleGameUI = new ConsoleGameUI();
GameController controller = new GameController(
consoleGameUI,
new Engine(),
new MoveGenerator());
consoleGameUI.init(new InputParser(),controller);
controller.start();
}
When we develop the
ConsoleGameUI class, we will write some unit tests. There we can use also the hijacked streams to control inputs and outputs. Because this time the test runs synchronously we can use a ByteArrayInputStream instead of PipedInputStream to supply the user input to the system under test:
public class ConsoleGameUITests {
// Class under test
ConsoleGameUI consoleGameUI;
private String userInput;
private ByteArrayInputStream inputStream;
private ByteArrayOutputStream outputStream;
...
@Before public void setUp() {
userInput = "some input from user";
inputStream = new ByteArrayInputStream(userInput.getBytes());
outputStream = new ByteArrayOutputStream();
System.setIn(inputStream);
System.setOut(new PrintStream(outputStream));
...
consoleGameUI = new ConsoleGameUI();
consoleGameUI.init(inputParserMock, gameControllerMock);
}
@Test public void shouldPromptTheUserToEnterSpotToPlaceAPiece(){
consoleGameUI.askUserForMove(Turn.PLACE_WHITE);
assertTrue(outputStream.toString().contains("Please enter spot to place piece:"));
}
@Test public void shouldPromptTheUserToEnterSpotsToSlidePiece(){
consoleGameUI.askUserForMove(Turn.SLIDE_WHITE);
assertTrue(outputStream.toString().contains("Please enter spots to slide piece:"));
}
@Test public void shouldReadInputAndCallParser()
{
context.checking(new Expectations() {{
oneOf(inputParserMock).Parse(userInput);
...
}});
consoleGameUI.askUserForMove(Turn.PLACE_WHITE);
context.assertIsSatisfied();
}
...
}
In the
ConsoleGameUI class we use
System.out and
System.in:
public class ConsoleGameUI implements GameUI {
private final Scanner scanner;
private IInputParser parser;
private IGameController gameController;
public ConsoleGameUI(){
this.scanner = new Scanner(System.in);
}
public void init(IInputParser parser, IGameController gameController){...}
@Override
public MoveRequest askUserForMove(Turn turn) {
switch(turn){
case PLACE_WHITE:
System.out.print("Please enter spot to place piece:");
break;
case SLIDE_WHITE:
System.out.print("Please enter spots to slide piece:");
break;
...
String line = scanner.nextLine();
MoveRequest request = parser.Parse(line);
return request;
}
...
}
7 comments:
its curious, I read the same book and I've decided to implement a console game as well, Checkers.
Really helpful your stuff I was struggling quite a bit in how to test console ui. Ill follow quite a bit of you stuff.
Thanks a lot Sven,
David
I was very interested in the article , it’s quite inspiring I should admit. I like visiting your site since I always come across interesting articles like this one. Keep sharing! Regards. Read more about
Offshore software testing services
software testing services company
software testing services
Software Qa Services
quality assurance service providers
Performance testing services
Security testing services
software testing Companies
Thank you for sharing the article.
Selenium Training in Chennai | Certification | Online Courses
selenium training in chennai
selenium training in chennai
selenium online training in chennai
selenium training in bangalore
selenium training in hyderabad
selenium training in coimbatore
selenium online training
شركة تنظيف بيوت بالرياض
Windows 7 Crack Download 2022 ; Windows 7 Professional Product Key 32-Bit. Windows 7 Professional Product Key 64-Bit ; 24437-XVJQQ-F36R3-7HM2B- .Windows 7 Ultimate Product Key
Merry Christmas, my love. I'm so happy I get to spend this day with you as there's nobody else I'd rather spend it with. Your energy and love have . Christmas Message to Wife 2022
Post a Comment