2

I am trying to write a junit test to test a method that takes multiple user input. The method takes a person object and sets a rating for them, the first user input is a double value for the rating, the second input is a string with the value "Y" to confirm the change.

I am trying to use ByteArrayInputStream but it is not getting the second input, the error I am getting when i run the test is No line found.

I have identified the problem as I am using different methods to validate user input I have to create a new scanner each time so the second line is not being accepted

Is there a way to set the Scanner again for the second input?

This is my test code

 Person p = new Person();

    double input1 = 54.3;
    String input2 = "Y";

    String simulatedUserInput = input1 + 
    System.getProperty("line.separator")
    + input2 + System.getProperty("line.separator");


    System.setIn(new ByteArrayInputStream(simulatedUserInput.getBytes(StandardCharsets.UTF_8)));

    addRating(p);

    assertEquals(54.3, p.getMyRating(),0);

The method for adding the rating looks like this

    public static void addRating(Person p)
{

    double rating = Validation.validateRating("Please enter a rating for " + p.getName()); // validate input of double

    boolean confirmed = Validation.validateYesNo("Are you sure you wish to set " + p.getName() + "'s rating to " + rating + " Y/N");// confirm yes no

    if (confirmed) 
    {
        p.setMyRating(rating);
    }

}

Then I have a validation class to ensure correct user input, This is for the rating

    public static double validateRating(String str)
{
    Scanner in = new Scanner(System.in);
    double d = 0;
    boolean valid = false;
    while (!valid)
    {
        System.out.println(str);
        if (!in.hasNextDouble())
        {
            System.out.println("Not a valid number");
            in.nextLine();
        } else
        {

            d = in.nextDouble();

            if(d>=0 && d<=100)
            {
               valid = true; 
            }
            else
            {
                System.out.println("Rating must be between 0 and 100");
            }

        }
    }
    return d;
}

this is for confirming Y/N input

    public static boolean validateYesNo(String str)
{
    Scanner in = new Scanner(System.in);
    boolean YesNo = false;
    boolean valid = false;

    while (!valid)
    {
        System.out.println(str);
        String choice = in.next();
        if (choice.equalsIgnoreCase("Y"))
        {
            valid = true;
            YesNo = true;
        } else if (choice.equalsIgnoreCase("N"))
        {
            valid = true;
            YesNo = false;
        } else
        {
            System.out.println("Invalid input");
        }
    }
    return YesNo;
}
1
  • System.setIn( This seems ugly as hell for a unit test. Restructure your code to take an input String as a paramter, and call that method from the unit test. In live code, reading a string and passing it as a parameter is easy, so there's no need to actually test System.in any more. Commented Apr 23, 2017 at 16:33

1 Answer 1

2

You get unit testing wrong. You don't write your production code first; to later discover: this is really hard to test.

Instead: right from the beginning, you strive to write code that is as easy as possible. Ideally, you even write testcases before you write production code.

Because that helps you discovering the abstractions you need. In your case: in order to validate input values within your Person class, it should not be important, where those values are coming from.

In other words: you do not never ne jamais put System.in read calls into your production classes. You might have a test main method that reads values from the console; but you always pass such values into a method as parameter. If at all, you pass an instance Reader into your classes. You do not turn to System.in inside your methods!

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.