I am trying to create a junit test on a method that calls for a user input using scanner and System.in.
The method being tested looks like this:
public void setUserAnswer(){
Scanner input = new Scanner(System.in);
userAnswer = input.nextInt();
}
Currently, I am using the following set up in jUnit:
StringBuilder sb = new StringBuilder();
sb.append("");
sb.append((problem.getOperand1()+problem.getOperand2()));
String data = sb.toString();
System.setIn(new ByteArrayInputStream(data.getBytes()));
operand1 and operand2 are random generated numbers which the user adds and enters the answer for.
The problem I have is the System.setIn doesn't pass from the jUnit class to the class being tested.
Help?
To cut the discussion, your method could be tested in following way:
public class MathProblem {
private int userAnswer;
public void setUserAnswer(){
Scanner input = new Scanner(System.in);
userAnswer = input.nextInt();
}
public int getUserAnswer() {
return userAnswer;
}
}
and now the Test:
public class MathProblemTest {
private MathProblem mathProblem;
#Before
public void before() throws Exception {
mathProblem = new MathProblem();
}
#Test
public void testGetUserAnswer() throws Exception {
StringBuilder sb = new StringBuilder();
sb.append("41 20");
String data = sb.toString();
System.setIn(new ByteArrayInputStream(data.getBytes()));
mathProblem.setUserAnswer();
assertThat(mathProblem.getUserAnswer(), equalTo(41));
}
}
As the others already mention: This code behaves as expected.
So if you do not see a correct behaviour the test might be broken or the problem is in another region of your code, that you have not posted. So ask your self: Where does your example differ from this example?
You should not be messing with System.in during a unit test.
Ask yourself what it is you're testing. Are you testing the Scanner code, or the code that uses whatever values are entered by the user?
Your code probably does 3 things:
Ask user for input
Perform operation
Display result
In a unit test, you're testing the operation, not the user prompting or the display logic. Or if you do, they should be 3 different unit tests, and remember that if you're testing the user prompting, you're testing the use of the Scanner, not whether the Scanner can read System.in.
So, first split your code to:
public class MyClass {
public static void main(String[] args) {
Input input = promptUserForInput(new Scanner(System.in), System.out);
Result result = performOperation(input);
printResult(System.out, result);
}
// methods here
}
You don't have to create new classes for Input and Result if they are simple values. They could also be the same class, e.g. an instance of MyClass.
This way you can test if the operation works for various inputs, which is your primary concern.
#Test
public void testOperation1() {
Input input = new Input(5, 15, true); // true means subtract
Result result = MyClass.performOperation(input);
assertEquals(-10, result.getValue());
}
#Test
public void testOperation2() {
Input input = new Input(5, 15, false); // false means add
Result result = MyClass.performOperation(input);
assertEquals(20, result.getValue());
}
You can also test user prompting and result printing, if needed.
#Test
public void testPrompt() {
String userInput = "5 15\nYes";
PrintStream out = new PrintStream(new ByteArrayOutputStream());
Input input = MyClass.promptUserForInput(new Scanner(userInput), out);
assertEquals(5, input.getNum1());
assertEquals(15, input.getNum2());
assertTrue(input.isSubtractRequested());
}
#Test
public void testPrint() {
ByteArrayOutputStream buf = new ByteArrayOutputStream();
try (PrintStream out = new PrintStream(buf)) {
MyClass.printResult(out, new Result(-10));
}
String outText = new String(buf.toByteArray());
assertEquals("Result is -10\r\n", outText);
}
Related
I have two versions of Java code that gets user input until user types "q"
Version 1:
public class Test {
public static void main(String[] args) {
String input = "";
while (!input.equals("q")) {
Scanner scanner = new Scanner(System.in);
System.out.print("Input: ");
input = scanner.nextLine();
System.out.println("Input was: " + input);
}
}
}
Version 2:
public class Test {
public static void main(String[] args) {
String input = "";
while (!input.equals("q")) {
try(Scanner scanner = new Scanner(System.in)){
System.out.print("Input: ");
input = scanner.nextLine();
System.out.println("Input was: " + input);
}
}
}
}
Version 1 works as expected but version 2 does not work as expected.
That is after reading user input for the first time, it produces an error
Input: 12
Input was: 12Exception in thread "main"
Input: java.util.NoSuchElementException: No line found
at java.util.Scanner.nextLine(Scanner.java:1540)
at RealEstateCompany.main(RealEstateCompany.java:115)
My guess is since version 2 uses try with resource so it closes the scanner after being used and that is causing an error?
Thank you for your help in advance!
[Update]
Version 3:
public class Test {
public static void main(String[] args) {
String input = "";
try(Scanner scanner = new Scanner(System.in)){
while (!input.equals("q")) {
System.out.print("Input: ");
input = scanner.nextLine();
System.out.println("Input was: " + input);
}
}
}
}
Version 3 works. However, why version 3 is ok and version 2 is not ok?
Adding a little bit more detail to my comments
A try-with block is defined as follows:
try(...) {
...
}
where the argument in parenthesis needs to be an instance of java.lang.AutoCloseable. An example is the class java.io.InputStream, which is also the class for System.in.
A try-with attempts to automatically close its provided resource, once the block is left. Depending on the used resource, it closes all its own child resources as well.
Taking your example, you have try(Scanner scanner = new Scanner(System.in)), which uses Scanner as resource. The scanner itself uses System.in as resource. Once the try block is left (when } is reached) it tries to close its resources, which is the Scanner instance. This instance also tries to close its resource, the System.in.
Once System.in is closed, you can't get any input from the console anymore (at least not with some additional work, I think...).
Concretely, in your second example:
while (!input.equals("q")) {
try(Scanner scanner = new Scanner(System.in)){
...
} // <--- The block is left, scanner is closed, System.in is closed
} // <-- start a new iteration
Here after just one iteration, System.in gets closed. Sure, you create a new Scanner in the next iteration, but System.in remains closed, that's why you get your exception in this case.
Your third example:
try(Scanner scanner = new Scanner(System.in)){
while (!input.equals("q")) {
...
} // <-- start a new iteration, while still in the same try block
} // <-- only after the while, your resources are closed
Here you're looping your while, while still being inside try. So no resource gets closed, until you leave while and try. That means, the one Scanner remains intact and with it the one System.in. This allows you to keep reading from the console until you're done looping.
Try this:
String input = "";
try (Scanner scanner = new Scanner(System.in)) {
while (!input.equals("q")) {
System.out.print("Input: ");
input = scanner.nextLine();
System.out.println("Input was: " + input);
}
}
You can use every class thats implements Closeable or AutoCloseable in try-with-resources, When code reaches the end of the try call, It call close() function of the Scanner class in our example.
i run some tests and add the catch block into your code.here's the code
public static void main(String[] args) {
String input = "";
while (!input.equals("q")) {
try(Scanner scanner = new Scanner(System.in)){
System.out.print("Input: ");
input = scanner.nextLine();
System.out.println("Input was: " + input);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
when add the catch block,there are 2 kinds of results
1,only inputs q, works as expected
2,inputs any other String, exception
Input: java.util.NoSuchElementException: No line found
at java.util.Scanner.nextLine(Scanner.java:1585)
at rews.pub.Test.main(Test.java:11)
when added the catch block, we will see that the program won't stop, because of the while loop
here is another easier test
public class Test {
public static void main(String[] args) {
String input = "";
Scanner scanner = new Scanner(System.in);
System.out.println("inout--1---");
input = scanner.nextLine();
scanner.close();
Scanner scanner2 = new Scanner(System.in);
System.out.println("inout--2---");
input = scanner2.nextLine();
scanner2.close();
}
}
and it goes same exception
inout--1---
11
inout--2---
Exception in thread "main" java.util.NoSuchElementException: No line found
at java.util.Scanner.nextLine(Scanner.java:1585)
at rews.pub.Test.main(Test.java:15)
here's my opinion.
in the end of first run, try()block will close the resource which is in the block, means we close the system.in
system.in is a object of inputSteam,and system.in is final and static, we can't open it again like 'new Scanner(System.in)'
I'm trying to learn unit testing and Maven, to do so I'm using JUnit and writing simple random name generator. I have following class:
public class Database {
public String readRandomName(String url) throws FileNotFoundException {
int sum = calculateFileLines(url);
int lines = (int) (Math.random()*sum);
File file = new File(url);
Scanner scanner = new Scanner(file);
for (int i=0; i<lines;i++){
scanner.nextLine();
}
return scanner.nextLine();
}
public int calculateFileLines(String url) throws FileNotFoundException {
int sum = 0;
try (Scanner scanner = new Scanner(new File(url))){
while(scanner.hasNextLine() && scanner.nextLine().length()!=0){
++sum;
}
}
return sum;
}
}
When I run simple test like this:
public static void main(String[] args) throws FileNotFoundException {
Database database = new Database();
database.readRandomName("names/maleNamesPL.txt");
}
It works perfectly, but when I tried to write JUnit test with assertion there is an unexpected failure which I don't understand. This is test code:
#Test
public void readRandomNameTest() throws FileNotFoundException {
Database database = new Database();
Assert.assertNotNull("Should be some name", database.readRandomName("names/maleNamesPL.txt"));
}
And the results:
Tests in error:
readRandomNameTest(DatabaseTest): No line found
I would appreciate any help, thank you!
You're calling nextLine() and it's throwing an exception when there's no line, exactly as the javadoc describes. It will never return null
http://download.oracle.com/javase/1,5.0/docs/api/java/util/Scanner.html
With Scanner you need to check if there is a next line with hasNextLine()
so the loop becomes
while(scanner.hasNextLine()){
String str=scanner.nextline();
//...
}
I have a class which reads a file and takes in a user input with a scanner and if the scanner equals a part of a line in that file, it will display a string from the same line.
How would I go and create a Junit test method for this?
Here is some of my code that I want a test method for:
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];
System.out.println(sName);
}
}
}
my data file looks like this
||
*Jack_Davis
*Sophia_Harrolds
I have tried to use this code in my test method
#Test
public void testgetSurname() {
System.out.println("get surname");
String filename = "";
String expResult = "";
String result = fileReader.getSurname(filename);
assertEquals(expResult, result);
filename = "datafiles/names.txt";
String data = "Jack";
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);
expResult = "Davis";
}
String result = fileReader.getSurname(filename);
assertEquals(expResult, result);
}
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");
One way to do it:
Step 1
Refactor your code, so that Scanner is one of the parameters passed into your method.
Step 2
For your test, use constructor Scanner(File file) or Scanner(String source) to feed "what would the user type" - while in the real world (from your main() you'd create Scanner(System.in)
or
Refactor your code to get a protected Scanner getScanner() { } and then use a Mocking framework (I like Mockito) to mock that method and return your String-prepped Scanner (see Step 2)
Requested example (the refactoring)
/**
* Reads the next line from the Scanner
*/
protected String getNextLine(Scanner scanner) {
//You know how to do that.
}
/**
* Check if file contains that name and return matching line.
* Throws NameNotFoundException (you'd need to create) if not found.
*
* If it were my code, i'd refactor even more and pass in List<String>
* here with the lines from the file
*/
public String matchSurname(String name, File dataFile) throws NameNotFoundException {
//Iterate over file...
if(isMatchingSurname(name, line)) {
return line;
}
// end iteration
//Still here? Throw Exception!
throw new NameNotFoundException();
}
/**
* Checks if given Name matches the read line
*/
public boolean isMatchingSurname(String name, String lineFromFile) {
//All the checks
}
Now you've broken your problem down in nice small bites. Unit testing would now be for individual methods only - so one for testing reading a line from Scanner, one for testing the Matching logic and one for correct file iteration.
public static void main(String[] args) throws FileNotFoundException {
Scanner sc = new Scanner(new FileReader(args[1]));
String co = sc.next();
coup = Integer.parseInt(co);
I get a FileNotFoundException when I try to pass an int into the second argument in command line. This is only part of the code, a text file is passed as args[0]. However, I can't figure out how to pass a simple integer, only text files.
public static void main(String[] args) throws Exception
{
Scanner scan = new Scanner(new FileReader(args[0]));
int integerFromCM = Integer.parseInt(args[1]);
}
You state that a text file is the first argument (args[0]) so assign that in the scanner and when grabbing the integer all you need to do is send args[1] into Integer.parseInt method. You are getting the exception because you are assigning a FileReader object with the file name of the integer passed in.
You can't pass an int, but you can parse one:
public static void main(String[] args) {
String filename = args[0];
int i = Integer.parseInt(args[1]);
// ...
}
If you are getting a FileNotFoundException, one easy way to debug it is:
File file = new File(filename);
System.out.println(file.getAbsolutePath());
and it will be obvious where the problem lies, which is almost always the current directory of the application is not what you think it is.
Reviewing your code it reads as follows:
Create a Scanner to read the file in the first command line argument
Get the first integer from that Scanner as a String
Parse that String to an int
It is clearly sequenced to require a file from the first argument and that looks like it is intended.
Create a file called number.txt:
42
NumberPrinter.java:
import java.io.Scanner;
import java.io.FileReader;
public final class NumberPrinter {
public static void main(String[] args) throws Exception {
Scanner scanner = new Scanner(new FileReader(args[1]));
String numberInFile = scanner.next();
int number = Integer.parseInt(numberInFile);
System.out.println(number);
}
}
Run as follows:
java NumberPrinter number.txt
And it will print:
42
Alternatively if you intend to parse an int directly from the command line parameters try:
public final class NumberPrinterDirect {
public static void main(String[] args) throws Exception {
int number = Integer.parseInt(args[0]);
System.out.println(number);
}
}
NumberOrFilenameAwkward.java:
import java.io.Scanner;
import java.io.FileReader;
public final class NumberOrFilenameAwkward {
public static void main(String[] args) throws Exception {
int number;
try {
number = Integer.parseInt(args[0]);
} catch (NumberFormatException thisIsVeryUgly) {
Scanner scanner = new Scanner(new FileReader(args[1]));
String numberInFile = scanner.next();
number = Integer.parseInt(numberInFile);
}
System.out.println(number);
}
}
That is a terrible solution and screams for using a command line parsing library like JewelCLI or commons-cli to solve it cleanly.
I have the following problem: method readLine() or nextLine(), nextInt(), etc. throw an exception: NullPointerException.
I use the NetBeans IDE (if it matters).
public static void Reading()
{
String qq;
qq = System.console().readLine();
System.console().printf(qq);
}
Some IDEs don't provide a console. Note that System.console() returns null in these cases.
From the documentanion
Returns:
The system console, if any, otherwise null.
You can always use System.in and System.out instead, as follows:
String qq;
Scanner scanner = new Scanner(System.in);
qq = scanner.nextLine();
System.out.println(qq);
Two things:
The standard way of printing things is System.out.println("Thing to print");
The standard way of reading input off the console is: Scanner s = new Scanner(System.in); String input = s.nextLine();
So with these in mind, your code should be
public static void Reading() {
String qq;
Scanner s = new Scanner(System.in);
qq = s.nextLine();
System.out.println(qq);
s.close();
}
or
public static void Reading() {
String qq;
try (Scanner s = new Scanner(System.in)) {
qq = s.nextLine();
System.out.println(qq);
}
}