JUnit testing with simulated user input
To test drive your code, you should create a wrapper for system input/output functions. You can do this using dependency injection, giving us a class that can ask for new integers:
public static class IntegerAsker {
private final Scanner scanner;
private final PrintStream out;
public IntegerAsker(InputStream in, PrintStream out) {
scanner = new Scanner(in);
this.out = out;
}
public int ask(String message) {
out.println(message);
return scanner.nextInt();
}
}
Then you can create tests for your function, using a mock framework (I use Mockito):
@Test
public void getsIntegerWhenWithinBoundsOfOneToTen() throws Exception {
IntegerAsker asker = mock(IntegerAsker.class);
when(asker.ask(anyString())).thenReturn(3);
assertEquals(getBoundIntegerFromUser(asker), 3);
}
@Test
public void asksForNewIntegerWhenOutsideBoundsOfOneToTen() throws Exception {
IntegerAsker asker = mock(IntegerAsker.class);
when(asker.ask("Give a number between 1 and 10")).thenReturn(99);
when(asker.ask("Wrong number, try again.")).thenReturn(3);
getBoundIntegerFromUser(asker);
verify(asker).ask("Wrong number, try again.");
}
Then write your function that passes the tests. The function is much cleaner since you can remove the asking/getting integer duplication and the actual system calls are encapsulated.
public static void main(String[] args) {
getBoundIntegerFromUser(new IntegerAsker(System.in, System.out));
}
public static int getBoundIntegerFromUser(IntegerAsker asker) {
int input = asker.ask("Give a number between 1 and 10");
while (input < 1 || input > 10)
input = asker.ask("Wrong number, try again.");
return input;
}
This may seem like overkill for your small example, but if you are building a larger application developing like this can payoff rather quickly.
You can replace System.in with you own stream by calling System.setIn(InputStream in). InputStream can be a byte array:
InputStream sysInBackup = System.in; // backup System.in to restore it later
ByteArrayInputStream in = new ByteArrayInputStream("My string".getBytes());
System.setIn(in);
// do your thing
// optionally, reset System.in to its original
System.setIn(sysInBackup);
Different approach can be make this method more testable by passing IN and OUT as parameters:
public static int testUserInput(InputStream in,PrintStream out) {
Scanner keyboard = new Scanner(in);
out.println("Give a number between 1 and 10");
int input = keyboard.nextInt();
while (input < 1 || input > 10) {
out.println("Wrong number, try again.");
input = keyboard.nextInt();
}
return input;
}