How can I unit test classes that read input in Java? - java

How is it possible to run tests against a class that reads from System.in for user input?
For example:
private int getUserInput() {
Scanner scanner = new Scanner(System.in);
System.out.print("What's ya input? [1-3]: ");
return scanner.nextInt();
}
I've thought maybe subclassing the main class and overridding getUserInput to feed back scripted answers. Though this won't work if you need to read System.out to decide the input.
I spent a few hours looking up Threads however couldn't figure out how to use them for this either.

Well, you can replace System.in using System.setIn() but I would not favour that approach. Setting global state always makes your tests a bit more fragile and non-transparent.
Instead you should really try to break the dependency, for example by injecting the InputStream that you want to read from into your class through the class' constructor. Once you do that, when you unit test it you can pass in your own InputStream that reads from static data, and in your production code you can inject System.in.

Either put your references to System.in and System.out to variables or to result values of methods that you can override in your test (by other streams, like ByteArrayInput/OutputStream, or (if that is not possible) use System.setOut and System.setIn to replace the default streams, run your test, validate the output and set them back.

You can create Mock objects that imitates user input.

Related

How Scanner decides to wait for user input & why it doesn't wait for input with toString?

I was trying to capture the user input(a String) by using a Scanner object to process it further as required, while doing that I tried using a method which is not listed in standard methods of String class.
So my code looked like below :
Scanner user_input = new Scanner(System.in);
System.out.println("Please enter the string");
String captured_string = user_input.toString();
System.out.println(captured_string);
using .toString does not throw an error at all, but also the program does not wait for the user input.
I'm aware that using a .nextLine can solve the problem here as its a standard method defined for use in Scanner class.
Can someone please help understand, why the program does not wait for the user input ?
...while doing that I tried using a method which is not listed in
standard methods of String class.
Every class in Java inherits a class called, Object by default. The class, Object has a method called, toString which returns a String. It means that if a class does not override (i.e. redefine) the method, toString, calling this method on its object will print what Object#toString returns.
using .toString does not throw an error at all
Now that you have understood the concept of toString, I do not need to explain to you why it did not throw an error.
but also the program does not wait for the user input.
You have called the wrong method for this purpose. In order to wait for input, Scanner has the method, next and the methods starting with name next (e.g. nextLine, nextInt etc.) as per your requirement. I suggest you spend some time studying the documentation. A sample usage will be as follows:
String captured_string = user_input.nextLine();
You are required to use nextLine method because toString convert the object scanner to string that's why it prints this weird stuff.
The Scanner.toString() method returns information about the Scanner object itself, not any data from the input stream you are using. See the documentation:
Returns the string representation of this Scanner. The string representation of a Scanner contains information that may be useful for debugging. The exact format is unspecified.
When you want to read data from the input stream you have to use any next*() method like nextLine().

Java: How to AUTOMATICALLY test a console application that gets input from user

I'm writing a console application in Java that gets input from the user by
'Scanner' class. I don't want to enter each time that I test my program the input manually in the console, so I want to put all my inputs in a text file and make the Java program read this text file instead of typing again the inputs.
The way I found to do it is changing the line:
Scanner sc = new Scanner(System.in);
to:
Scanner sc = new Scanner(new File(path_to_text_file));
Can you recommend another way that doesn't involve changing the code?
Since you want to have this for testing, the best way would be to write a test for this class. Have a look at JUnit.
In such a test you could provide a different Scanner to you original class, either by using
Scanner sc = new Scanner(new File(path_to_text_file));
or you could use a framework like Mockito in order to fake different user inputs through Scanner and then check whether these inputs produced the correct outputs.
If you absolutely can't change your code, use System.setIn to replace the default input stream with one of your choosing.
Be sure always to change the stream back to the original one once you've finished testing - in doing this, you're mutating global state, so you can leave things in a bad state for the rest of your tests.
InputStream previousIn = System.in;
InputStream replacement = new FileInputStream(path_to_text_file);
System.setIn(replacement);
try {
// ... Do your testing.
} finally {
System.setIn(previousIn);
}
Or, change the design of your code so that you inject the input stream that you depend on. This avoids the mutation of global state, which is a good thing.

life and death of an object explanation

The code below gives me a warning on Eclipse: "resource leak, input is never closed":
Code: 1
import java.util.Scanner;
public class Ex {
public void sum() {
Scanner input = new Scanner(System.in);
}
}
I found that declaring the input variable in the class level does the trick, the warning is gone.
But, is this the right way of fixing this issue? And what is the difference between the second code and the third code? Does code 3 means that I have abandoned the first object?
Code: 2
import java.util.Scanner;
public class Ex {
private Scanner input;
public void sum() {
input = new Scanner(System.in);
}
}
Code: 3
import java.util.Scanner;
public class Ex {
private Scanner input = new Scanner(System.in);
public void sum() {
input = new Scanner(System.in);
}
}
but , is this the right way of fixing this issue - No its not... The input is still not closed, only that Eclipse is not able to detect it anymore... The issue is still present...
That being said, it's not a good idea to close the Standard input System.in too (closing the Scanner object would close the underlying stream too as I understand)... Hence it would be better to leave it like that even though there is a warning...
Yes, since in code 3 you have reinitialized input, you have abandoned the first object.
The presence of a warning is almost always a sign of a problem. But the absence of a warning doesn't mean there is no problem.
The first snippet warns you that you're not closing the Scanner object when you're done with it. The proper fix would be to close it. Not to make your code worse by using a field instead of a local variable, and still not closing the scanner when you're done with it. That will make the warning disappear, but not the problem. Variables should always have the narrowest possible scope.
That said, A Scanner opened with System.in as argument, although it implements Closeable (that's why you have the warning), should in fact not be closed, because that would close the underlying stream: System.in. File or Socket streams must be closed, but System.in should generally not. If you close it, the user won't be able to enter anything anymore to your program.
You are getting the warning because the scanner instance is never closed. Modify the code: 1 as below to eliminate the warning,
public void sum() {
Scanner input = null;
try {
input = new Scanner(System.in);
// do operation
} finally {
input.close();
}
}
Regarding code:2, declare any variable at Object level only when its required. i.e if it needs to be accessed by multiple member functions defined in the class.
To answer your question is this the right way of fixing this issue , and what is the difference between the second code and the third code ? Does code : 3 means that i have abandoned the first object ?
No, Code: 3 is not the right way way to fix the issue. And yes, code:3 creates a new instance of scanner inside the member function "sum" and the reference of the member variable input will point to the newly created scanner instance.
In summary, create a member variable (object level) only when its absolutely required, otherwise create a local variable - this will ensure that the objects get collected much earlier. (i.e. these objects will become eligible for GC when the method execution completes). Also, make sure that, call close() on almost all of the IO classes (stream classes) that we operate on.
Also, Note that if you close System.in, you won't be able to read from it again

Attempt to TDD and unit test text user input from console for Java

I'm writing a unit test for the following method of my tic-tac-toe program. I would like to follow a TDD approach but the method requires user input.
public int[] playerMove(){
Scanner reader = new Scanner(System.in);
int[] move = new int[2];
move[0] = reader.nextInt()-1;
move[1] = reader.nextInt()-1;
return move;
}
My problem is that I can't input test numbers for move[0] and move[1] since it requires user input which is supplied via System.in. How would simulate this during my test?
First of all, if you already have the code written, and only now you're writing the test, it's not TDD.
Regarding your problem, one way to solve this is to have the InputStream passed as a parameter to the constructor to the class you're testing, rather than hard-coding System.in.
This will enable you to create an instance of the tested class with a mock InputStream that generates whichever input you want.

Jmock - how to automate & mock out console user input?

I have some functionality that I want to mock out being called from main (static: I've read about that too - jmock mocking a static method). i recently read that JMock doesn't support the mocking of static functions. Well, the associated code (that's giving me a problem) must be called from main, and must be in the class with main...
Sample source
Test code
Right now, I want to ensure that my main has a test to make sure that the file exists before it proceeds. Problem is, I have my program getting user input from the console, so I don't know how to mock that out? Do I just go down to that level of granularity, specifying at every point along the way what happens, so that I can write about only one operation in a function that returns the user's input? I know that to write the tests well, when the tests are run, they should not ask for the user input, I should be specifying it in my tests somehow.
I think it has to do with the following:
How to use JMock to test mocked methods inside a mocked method
I'm not that good with JMock...
If the readInput() method does something, like, say:
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
return in.readLine();
Then you might be able to get away with a test that goes something like:
InputStream oldSystemIn = System.in;
InputStream mockSystemIn = context.mock(InputStream.class);
System.setIn(mockSystemIn);
context.checking(new Expectations() {{
// mock expected method calls and return values
}});
// execute
// verify
System.setIn(oldSystemIn);
You can use System Rules instead of mocking System.out and System.in.
public void MyTest {
#Rule
public TextFromStandardInputStream systemInMock = emptyStandardInputStream();
#Test
public void readTextFromStandardInputStream() {
systemInMock.provideText("your file name");
//your code that reads "your file name" from System.in
}
}
Stefan Birkner's answer gave me the direction that I need to be able to solve this. I have posted the code that I used to solve this below.
Solved tests: Birkner's version (recommended)
Solved tests: piped version
Changed source:
WHY: What happens is, with Birkner's library, you can only ever read as much input as you instantiate with the rule originally. If you want to iteratively write to the endpoint, you can do this with a pipe hack, but it doesn't make much of a difference, you can't write to the input over the pipe while the function is actually running, so you might as well use Birkner's version, his #Rule is more concise.
Explanation: In both the pipe hack and with Birkner's code, in the client being tested, multiple calls to create any object that reads from System.in will cause a blocking problem where, once the first object has opened a connection to the Pipe or to System.in, others can not. I don't know why this exactly is for Birkner's code, but with the Pipe I think that it's because you can only open 1 stream to the object-ever. Notice that if you call close on the first buffered reader, and then try to reopen System.in in your client code after having called it from the test, then the second attempt to open will fail because the pipe on the writer's side has been closed as well.
Solution: Easy way to solve this, and probably not the best because it requires modifying the source of the actual project, but not in a horrendous way (yet). So instead of having in the source of the actual project multiple BufferedReader creations, create a buffered reader, and pass the same reader reference around or make it a private variable of the class. Remember that if you have to declare it static that you should not initialize it in a static context because if you do, when the tests run, System.setIn will get called AFTER the reader has been initialized in your client. So it will poll on all readLine/whatever calls, just as it will if you try to create multiple objects from System.in.
Notice that to have your reads segregated between calls from your reader, in this case BufferedReader, you can use newlines to segregate them in the original setup. This way, it returns what you want in each call in the client being tested.

Categories

Resources