I have to test a method in a class which takes an input using Scanner class.
package com.math.calculator;
import java.util.Scanner;
public class InputOutput {
public String getInput() {
Scanner sc = new Scanner(System.in);
return sc.nextLine();
}
}
I want to test it using JUnit but not sure how to do it.
I tried using the following code but it wont work.
package com.math.calculator;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class InputOutputTest {
@Test
public void shouldTakeUserInput() {
InputOutput inputOutput= new InputOutput();
assertEquals("add 5", inputOutput.getInput());
}
}
I want to also try it with Mockito (using mock... when ... thenReturn) but not sure how to do it.
You can change the System.in
stream using System.setIn()
method.
Try this,
@Test
public void shouldTakeUserInput() {
InputOutput inputOutput= new InputOutput();
String input = "add 5";
InputStream in = new ByteArrayInputStream(input.getBytes());
System.setIn(in);
assertEquals("add 5", inputOutput.getInput());
}
You have just modified the System.in
field. System.in
is basically an InputStream
which reads from the console
(hence your input in the console). But you just modified it and let the system to read from the provided inputstream
instead. So it wont read from console anymore but from the inputstream provided.
You can write a clear test for the command line interface by using the TextFromStandardInputStream
rule of the System Rules library.
public void MyTest {
@Rule
public final TextFromStandardInputStream systemInMock
= emptyStandardInputStream();
@Test
public void shouldTakeUserInput() {
systemInMock.provideLines("add 5", "another line");
InputOutput inputOutput = new InputOutput();
assertEquals("add 5", inputOutput.getInput());
}
}
In addition to switching System.in, as Codebender also mentioned, consider refactoring so getInput()
becomes a one-line call to a thorough getInput(Scanner)
method you write, which you could easily test by creating your own Scanner("your\ntest\ninput\n")
. There are a number of other ways to inject your scanner dependency, like making a field you overwrite for testing, but just making a method overload is extremely easy and technically gives you more flexibility (letting you add a feature to read input from a File, for instance).
In general, remember to design for ease of testing, and test the high-risk parts more heavily than the low-risk parts. This means that refactoring is a good tool, and that testing getInput(Scanner)
is likely much more important than testing getInput()
, especially as you do more than just calling nextLine()
.
I would recommend heavily against creating a mock Scanner: Not only is it bad practice to mock a type you don't own, but Scanner represents a very large API of interrelated methods where call order matters. To replicate it in Mockito means that either you would create a big fake Scanner implementation in Mockito or mock a minimal implementation that tests only the calls you make (and breaks if your implementation changes, even if your changes provide a correct result). Use a real Scanner and save Mockito practice for external service calls or cases where you're mocking a small yet-unwritten API you define.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With