'Junit method testing, user integer input
I don't know how to simulate user input for integers/floats/doubles inside a unit test. I used this to simulate a string input:
@Test
public void testSetName() {
String expectedResult = "Jason";
InputStream in = new ByteArrayInputStream(expectedResult.getBytes());
System.setIn(in);
assertEquals(expectedResult, MainClass.setName());
}
But I don't know how to enter an integer instead. This is the method I am testing:
public static int setAge() {
Scanner input = new Scanner(System.in);
int age = 0;
boolean done = false;
while (!done) {
System.out.print("Age: ");
try {
age = input.nextInt();
done = true;
} catch (InputMismatchException e) {
System.out.println("Please enter a valid age!");
input.nextLine();
}
}
return age;
}
Solution 1:[1]
I will use a random test example to explain the logic. 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 (In this example I am using 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;
}
Solution 2:[2]
So I figured it out. It can be done the same exact way as a string as this is just sending an input and not a specific type. In my case, the method I want to test is:
public static int setAge() {
Scanner input = new Scanner(System.in);
int age = 0;
boolean done = false;
while (!done) {
System.out.print("Age: ");
try {
age = input.nextInt();
done = true;
} catch (InputMismatchException e) {
System.out.println("Please enter a valid age!");
input.nextLine();
}
}
return age;
}
and the test I wrote is this:
@Test
public void testSetAge() {
String expectedResult = "5";
InputStream in = new ByteArrayInputStream(expectedResult.getBytes());
System.setIn(in);
assertEquals(5, MainClass.setAge());
}
I am not sure if this is the best/proper way to complete this but it was how I did it.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | David Kariuki |
| Solution 2 | BiggerOwO |
