13

In our application, we expect user input within a Thread as follows :

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

I want to pass that part in my unit test so that I can resume the thread to execute the rest of the code. How can I write something into System.in from junit?

jjnguy
  • 136,852
  • 53
  • 295
  • 323
Mesut
  • 916
  • 1
  • 7
  • 17
  • 2
    I didn't think you could, but apparently you can according to Justin. However, wouldn't it be better to refactor the tested code to get the inputstream inserted from outside? – Bart van Heukelom Sep 28 '10 at 14:59
  • @Bart, if you are testing an app that takes input from `System.in`, eventually you will want to test the module that takes the data from `System.in`. – jjnguy Sep 28 '10 at 15:15
  • 2
    @Justin Isn't this testing Java and not your code? If you make the InputStream injected you are still testing all of your code and depending on Java to provide a correct System.in. – Jacob Tomaw Sep 28 '10 at 15:47
  • 1
    @Justin: The two answers suggesting redirecting `System.in` are not really doing a complete job of that though, since System.in no longer reads from standard in which is what the application will be using. You're really not testing anything except for maybe whether the System class functions properly, which I assure you has been tested by Sun. – Mark Peters Sep 28 '10 at 15:48
  • 1
    @Jacob, no. You are testing how your code handles various inputs from `System.in`. You can automate the tests instead of having users enter in test data through a console. – jjnguy Sep 28 '10 at 15:51
  • @Mark, The application will be using the inputstream that `System.in provides`. The application shouldn't care where that data is coming from. When run in a production setting, it will be coming from a keyboard, but in the test, it will be coming from a text file, or some hard coded strings. You are testing the ability of your application to handle different types of inputs. – jjnguy Sep 28 '10 at 15:53

4 Answers4

30

What you want to do is use the method setIn() from System. This will let you pass data into System.in from junit.

jjnguy
  • 136,852
  • 53
  • 295
  • 323
  • 1
    This works when I run the JUnit tests individually, but when I run the entire class my program seems to stall at the points where the setIn() should be working. Does anyone else have this issue, and if so any ideas on how to get around it? – decal May 30 '13 at 21:07
  • @decal you problem may have been incorrect setup() & tearDown() modifying those values System.in() and System.out(). There are some other references to that problem elsewhere on SE. If anyone else sees this comment I'll search and replace.. – Crowie Mar 28 '17 at 01:27
11

Replace it for the duration of your test:

String data = "the text you want to send";
InputStream testInput = new ByteArrayInputStream( data.getBytes("UTF-8") );
InputStream old = System.in;
try {
    System.setIn( testInput );

    ...
} finally {
    System.setIn( old );
}
Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
  • Small detail: since the bufferedreader is constructed without a charset, shouldn't you call `getBytes()` without a charset as well (or, of course, add a charset to the bufferedreader)? – Bart van Heukelom Sep 28 '10 at 15:01
7

Instead of the suggestions above (edit: I noticed that Bart left this idea in a comment as well), I would suggest making your class more unit testable by making the class accept the input source as a constructor parameter or similar (inject the dependency). A class shouldn't be so coupled to System.in anyway.

If your class is constructed from a Reader, you can just do this:

class SomeUnit {
   private final BufferedReader br;
   public SomeUnit(Reader r) {
       br = new BufferedReader(r);
   }
   //...
}

//in your real code:
SomeUnit unit = new SomeUnit(new InputStreamReader(System.in));

//in your JUnit test (e.g.):
SomeUnit unit = new SomeUnit(new StringReader("here's the input\nline 2"));
Mark Peters
  • 80,126
  • 17
  • 159
  • 190
3

My solution currently (in 2018) is:

 final byte[] passCode = "12343434".getBytes();
 final ByteArrayInputStream inStream = new ByteArrayInputStream(passCode);
        System.setIn(inStream);

[Update in 2019] For JUnit4 Tests there is a framework for these tasks: https://stefanbirkner.github.io/system-rules/ (the upgrade to JUnit5 is on going: https://github.com/stefanbirkner/system-rules/issues/55)

Huluvu424242
  • 756
  • 10
  • 25