Junit: How to Simulate System.In Testing

JUnit: How to simulate System.in testing?

It is technically possible to switch System.in, but in general, it would be more robust not to call it directly in your code, but add a layer of indirection so the input source is controlled from one point in your application. Exactly how you do that is an implementation detail - the suggestions of dependency injection are fine, but you don't necessarily need to introduce 3rd party frameworks; you could pass round an I/O context from the calling code, for example.

How to switch System.in:

String data = "Hello, World!\r\n";
InputStream stdin = System.in;
try {
System.setIn(new ByteArrayInputStream(data.getBytes()));
Scanner scanner = new Scanner(System.in);
System.out.println(scanner.nextLine());
} finally {
System.setIn(stdin);
}

How to mock System.in?

This test works fine for me:

@Test
public void testBarCorrect() {
System.setIn(new ByteArrayInputStream("7\n2\n3".getBytes()));
Scanner scanner = new Scanner(System.in);
System.out.println(scanner.nextLine());
System.out.println(scanner.nextLine());
System.out.println(scanner.nextLine());
}

However, if I add the following test, this fails:

@Test
public void testFooCorrect() {
Scanner scanner = new Scanner(System.in);
System.out.println(scanner.nextLine());
System.out.println(scanner.nextLine());
System.out.println(scanner.nextLine());
}

java.util.NoSuchElementException: No line found
at java.util.Scanner.nextLine(Scanner.java:1540)
at com.amazon.adcs.service.dra.domain.A9DeviceTest.testFooCorrect(A9DeviceTest.java:15)

In theory a unit test should leave the JVM in the same state it was before the execution of the test. It's not your case since you have mutated a singleton state, and you don't rollback the modification after the test. If you have multiple tests using System.in, the first test might be consuming all of it and leaving nothing to the other one.

Normally, I would recommend you to inject an InputStream in your class and then you're free to do whatever you want without affecting a system constant. Since you tell me that you cannot edit the code, then you should make sure you clean up the state after yourself.

Something like this will do the trick:

private static final InputStream DEFAULT_STDIN = System.in;

@After
public void rollbackChangesToStdin() {
System.setIn(DEFAULT_STDIN);
}

If you have to do this often, you might want to consider implementing a JUnit rule.

Junit test of method with Scanner and System.in (Java)

Pass a Scanner as input parameter to the method you want to test.
In your test code, you can create a Scanner instance from a string:

Scanner scanner = new Scanner("the sample user input");

And then in the production code, you can pass new Scanner(System.in) to the method.

JUnit testing with simulated user input

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;
}

How to JUnit test a Method with a Scanner?

try this for example:

You could enhance it by simulate the console automatically (see below)

@Test
public void test_scan() throws Exception
{
Myclass myobject=new myobject(); // with args

myobject.load(filename); // you must definie the filename

String result=myobject.scaninput_and_compare(); // you must use scan in, and compare

if (!result.equals(what_I_am_expecting) throw new Exception("EXCEPTION scaninput_and_compare");

// If you arrive here, it's OK
}

If you want to automatize the console input, use that:

Courtesy of: JUnit: How to simulate System.in testing?

String data = "What_I_could_put_in_console";
InputStream stdin = System.in;
System.setIn(new ByteArrayInputStream(data.getBytes()));
Scanner scanner = new Scanner(System.in);
System.setIn(stdin);

Beware of catch Exception inside, to finish with a "good" System.in It's ok for a test alone, for several, you should verify.

With your code:

public String scaninput_and_compare(String filename)
{
Scanner Input = new Scanner(System.in);
String name = Input.nextLine();

BufferedReader br;
try{
br = new BufferedReader(new FileReader(new File(filename)));
String nextLine;
while ((nextLine = br.readLine()) != null)
{
if (nextLine.startsWith("||"))
{
int f1 = nextLine.indexOf("*");
int f2 = nextLine.indexOf("_");
fName = nextLine.substring(f1+1, f2);
if (name.equals(fname))
{
String[] s1 = nextLine.split("_");
String sName = s1[1];
return sName;
}
}
}
// NO GOOD
return "lose";
}

@Test
public void test_scan() throws Exception
{
Myclass myobject=new myobject(); // with args

String filename="good_filename";

// MOCK System.in
String data = "Jack";
InputStream stdin = System.in;
System.setIn(new ByteArrayInputStream(data.getBytes()));

String result=myobject.scaninput_and_compare(filename); // you must use scan in, and compare

// RESTABLISH System.in
Scanner scanner = new Scanner(System.in);
System.setIn(stdin);

if (!result.equals("Davis") throw new Exception("EXCEPTION scaninput_and_compare");

// If you arrive here, it's OK
}

BETTER design, and testing more easy: separate your scanner of System.in, from your file parsing. Just do a function with (filename, fname), and it will be direct to test :

assertEquals(myobject.scaninput_and_compare(filename,"Jack"), "Davis");


Related Topics



Leave a reply



Submit