views:

614

answers:

4

I have a Java command-line program. I would like to create Junit test case to be able to simulate System.in. Because when my program runs it will get into the while loop and waits for input from users. How do I simulate that in JUnit? Thanks

A: 

You could create a custom InputStream and attach it to the System class

class FakeInputStream extends InputStream {

    public int read() {
         return -1;
    }
}

And then use it with your Scanner

System.in = new FakeInputStream();

Before:

InputStream in = System.in;
...
Scanner scanner = new Scanner( in );

After:

InputStream in = new FakeInputStream();
...
Scanner scanner = new Scanner( in );

Although I think you should better to test how your class should work with the data read from the input stream and not really how it reads from there.

OscarRyz
From a TDD perspective, this avoids the design that the test is "driving" or attempting to indicate. However, the OP didn't specify TDD, and from a test-after perspective, it is a very reasonable thing to do - take advantage of the system global.
Yishai
You can't just do System.in = xxx as System.in is final. You can use System.setIn, but make sure you return to the default in the tear down. Also you don't need to roll your own InputStream, ByteArrayInputStream will do the job nicely.
mlk
Oohh yeap, I got confused. What I tried to say was ... well I will edit my entry :)
OscarRyz
+2  A: 

Try to refactor your code to use dependency injection. Instead of having your a method that uses System.in directly, have the method accept an InputStream as an argument. Then in your junit test, you'll be able to pass a test InputStream implementation in place of System.in.

Asaph
+3  A: 

There are a few ways to approach this. The most complete way is to pass in an InputStream while running the class under test which is a fake InputStream which passes simulated data to your class. You can look at a dependency injection framework (such as Google Guice) if you need to do this a lot in your code, but the simple way is:

 public class MyClass {
     private InputStream systemIn;

     public MyClass() {
         this(System.in);
     }

     public MyClass(InputStream in) {
         systemIn = in;
     }
 }

Under test you would call the constructor that takes the input stream. You cloud even make that constructor package private and put the test in the same package, so that other code would not generally consider using it.

Yishai
+1. I'm with you on this. I would go a step further: `InputData` as a high level wrapper around `InputStream` in unit testing, you should care more about what your class does, and not really about the integration.
OscarRyz
+3  A: 

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