Ideally, I would like to write JUnit test code that interactively tests I / O applications for text input. Using System.setIn() / .setOut() leads to problems as the underlying threads are blocked. The Birkner system rules ( http://www.stefan-birkner.de/system-rules/index.html ) were recommended in an earlier post ( Console Application / Program Testing - Java ), but it seems that all standard input was provided before the unit test goal was run and therefore was not interactive.
To provide a concrete example of a test goal, consider this guessing game code:
public static void guessingGame() { Scanner scanner = new Scanner(System.in); Random random = new Random(); int secret = random.nextInt(100) + 1; System.out.println("I'm thinking of a number from 1 to 100."); int guess = 0; while (guess != secret) { System.out.print("Your guess? "); guess = scanner.nextInt(); final String[] responses = {"Higher.", "Correct!", "Lower."}; System.out.println(responses[1 + new Integer(guess).compareTo(secret)]); } }
Now imagine a JUnit test that will give guesses, read answers and play a game until completion. How can this be done as part of JUnit testing?
Answer:
Using the approach recommended by Andrew Charnsky below, adding reset output (including adding System.out.flush(); after each print statement above), randomly playing and restoring System.in/out, this code seems to run the test I imagined:
@Test public void guessingGameTest() { final InputStream consoleInput = System.in; final PrintStream consoleOutput = System.out; try { final PipedOutputStream testInput = new PipedOutputStream(); final PipedOutputStream out = new PipedOutputStream(); final PipedInputStream testOutput = new PipedInputStream(out); System.setIn(new PipedInputStream(testInput)); System.setOut(new PrintStream(out)); new Thread(new Runnable() { @Override public void run() { try { PrintStream testPrint = new PrintStream(testInput); BufferedReader testReader = new BufferedReader( new InputStreamReader(testOutput)); assertEquals("I'm thinking of a number from 1 to 100.", testReader.readLine()); int low = 1, high = 100; while (true) { if (low > high) fail(String.format("guessingGame: Feedback indicates a secret number > %d and < %d.", low, high)); int mid = (low + high) / 2; testPrint.println(mid); testPrint.flush(); System.err.println(mid); String feedback = testReader.readLine(); if (feedback.equals("Your guess? Higher.")) low = mid + 1; else if (feedback.equals("Your guess? Lower.")) high = mid - 1; else if (feedback.equals("Your guess? Correct!")) break; else fail("Unrecognized feedback: " + feedback); } } catch (IOException e) { e.printStackTrace(consoleOutput); } } }).start(); Sample.guessingGame(); } catch (IOException e) { e.printStackTrace(); fail(e.getMessage()); } System.setIn(consoleInput); System.setOut(consoleOutput); }
source share