1

Currently writing JUnit tests but getting stuck when it comes to testing methods which rely on user input. Is there a way I can 'mock' user input in the test method? I have something like this (I did not paste the actual code since the method is quite long, and goes beyond the scope of understanding the problem; however, I do ask for user input, and if it is wrong, I ask for it again until a correct value is entered):

   public void method(){
    System.out.println("Enter something");
    int a = scanner.nextInt();
    for(;;){
       if(a!=10){
         System.out.println("Please enter another value!");
         a=sc.nextInt();
       else{
         break;
       }
    }
    //further code
   }
Katia Abela
  • 271
  • 7
  • 17

2 Answers2

1

Because this is interactive, it will likely be hard to test well—or, at least, in a way that is readable.

One common way to test similar code would be to extract a method that takes in a Scanner and a PrintWriter, similar to this StackOverflow answer, and test that:

public void method() {
  method(new Scanner(System.in), System.out);
}

/** For testing. */
public void method(Scanner scanner, PrintWriter output) {
  output.println("Enter something");
  int a = scanner.nextInt();
  // ...
}

This is similar to assigning a new stdin/stdout using System.setIn, but is much more predictable, and requires less cleanup. However, in both situations, methods like nextInt will block while waiting for input. Unless you want to make this test multithreaded (which is complicated), you won't be able to read your output until the end, and you'll have to specify all of your instructions up front:

@Test
public void methodShouldLaunchTheSpaceShuttle() {
  StringWriter output = new StringWriter();
  String input = "5\n"        // "Please enter another value!"
               + "10\n"       // "Code accepted. Enter command:"
               + "Shuttle\n"; // "Launching space shuttle..."
  systemUnderTest.method(new Scanner(input), new PrintWriter(output));

  assertThat(output.toString(), contains("Please enter another value!"));
  assertTrue(systemUnderTest.spaceShuttleLaunched());
}

That said, as long as your set of instructions is straightforward (and doesn't change), you can probably add comments (as above) and get a worthwhile test that increases your code coverage to what you need it to be.

(Note: Of course, rather than creating an overload method, you could also keep the "scanner" and "output" as mutable fields in your system under test. I tend to like keeping classes as stateless as possible, but that's not a very big concession if it matters to you or your coworkers/instructor.)

Community
  • 1
  • 1
Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
0

Can't you just supply your own test values? They're just ints right? You don't need to mock anything if they're primitive values, you can just test your functionality given a certain set of ints:

public void method(){
  int a = // your own test value, or even an array of test values
  for(;;) {
    if (a != 10){
      // check against your test values
    else{
      break;
     }
  }
  //further code
}

If you rely on external input to get your unit tests to work, those aren't really unit tests.

yamafontes
  • 5,552
  • 1
  • 18
  • 18