All,
I have written a PhoneBook application in Java that is command line based. The application basically asks for some details of user like Name, Age, Address and phone numbers and stores them in a file. Other operations involve looking up PhoneBook by name, phone number etc. All the details are entered through console.
I am trying to write JUnit test cases for each of the functionalities that I have implemented but not able to figure out how to redirect System.in
in the implementation code to something in my JUnit test methods that would supply those values when my actual code stops for user input?
Example:
My implementation code has:
BufferedReader is = new BufferedReader (new InputStreamReader(System.in));
System.out.println("Please enter your name:");
String name = is.readLine(); // My test cases stop at this line. How can I pass command line values i.e. redirect System.in to my test based values?
Hope it makes sense
This takes a basic looping console application and makes it testable, using the ideas from oxbow_lakes' answer.
The class-proper:
...continued...
The test (JUnit4):
...continued...
Why not write your application to take a
Reader
as input? That way, you can easily replace anInputStreamReader(System.in)
with aFileReader(testFile)
And then two instances:
Getting used to coding to an interface will bring great benefits in almost every aspect of your programs!
Note also that a
Reader
is the idiomatic way to process character-based input in Java programs.InputStream
s should be reserved for raw byte-level processing.System.setIn(new BufferedInputStream(new FileInputStream("input.txt")));
I suggest you to separate the code into three parts:
name
in your example)You do not need to test reading input and printing results, as that's Java code that is already tested by people writing Java.
The only thing you need to test is the thing you are doing, whatever that is. Unit tests are named like that because they tests units of code in isolation. You don't test the whole program, you test small pieces that are self-contained and have a well-defined function.
In unit tests, you should not rely on input/output operations. You should provide inputs and expected outputs directly in the unit test. It is sometimes convenient to use File reading operations to supply input or output (e.g. if the amount of data is huge), but as a general rule the more you go into input/output in your unit tests, the more complex they become and you are more likely not to do unit, but integration tests.
In your case, you use
name
somehow. If that is the only parameter, then make a method - let's call itnameConsumer
- that takes that name, does something and returns its result. In your unit tests, do something like this:Move your
println
andreadLine
calls to other methods and use aroundnameConsumer
, but not in your unit tests.Read more about this here:
Keep it simple, it pays off.
The library System Rules provides the rule TextFromStandardInputStream for simulating input in JUnit tests.
For details have a look at the System Rules documentation.