3

I have only one method main. How to check System.out.println() and replace Scanner to input values automatically using JUnit?

P.S. Please, provide some solutions...

public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    int[] arr = new int[4];

    for (int i = 0; i < arr.length; i++) {
        arr[i] = scanner.nextInt();
    }

    for (int i = 0; i < arr.length; i++) {
        int res = 0;
        int k = 0;
        int num = arr[i];
        /*.....*/
        System.out.println(num);
    }
}
Pretto
  • 467
  • 5
  • 18

2 Answers2

7

Ideally, extract the awkward dependencies so that you can test without them. Change main to simply:

public static void main(String[] args) {
  doWork(new Scanner(System.in), System.out);
}

// TODO: Rename to something meaningful
public static void doWork(Scanner input, PrintStream output) {
  // Remainder of code
}

(Consider using a Writer instead of a PrintStream for output.)

Then you don't really need to unit test main - but you can test doWork using a Scanner based on a StringReader, and output based on a StringWriter, providing whatever input you want and checking the output.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 2
    How can I use this to have for example, multiple inputs corresponding to multiple prompts for information? – CodyBugstein Oct 19 '15 at 21:08
  • I have the same question. Let me know if you found something...! – Aman Nov 15 '18 at 06:38
  • @Aman: Sorry, I must have not seen this comment before. You'd provide a `Scanner` that returns all the values you'd expect the user to enter, basically. – Jon Skeet Nov 15 '18 at 07:27
  • 1
    Thanks. But I used the System Rules library https://stefanbirkner.github.io/system-rules/ – Aman Nov 16 '18 at 03:57
2

I faced a similar issue and this is what I ended up doing.

First off, I'd suggest doing as @Jon-Skeet suggests and instead of using the main(String[]) method of the class, create a separate method.

Then you can have that method take in an InputStream as a parameter and then create a Scanner object within the method that uses the passed InputStream as its source. That way you can pass any InputStream, such as System.in, to the method when it's called (elaboration below).

package my.package;

import ...;

public class MyClass
{
    public static void myMethod(InputStream inputStream)
    {
        Scanner inputScanner = new Scanner(inputStream);

        // Do stuff with the Scanner such as...
        String input = inputScanner.nextLine();
    
        System.out.println("You inputted " + input);
    }
}

Now, in your production source code you can call myMethod and pass it System.in as an argument as such, myMethod(System.in);

And then in your unit tests, you can create mock input values via a ByteArrayInputStream:

package my.package;

import ...;

public class MyClassTest
{
    @Test
    void testMyMethod()
    {
        // Simulates a user inputting the string "Mock input" and hitting enter
        assertDoesNotThrow(myMethod(new ByteArrayInputStream("Mock input\n".getBytes())));
    }
}

And voila, you now have a way to pass your method mock input as well as it being more modular overall.

I just want to point out without getting too much into it, that when working with System.in, one needs to be careful about closing it and in unit tests when working with input streams, one needs to be careful about reusing a reference to the same InputStream as its state can persist across uses.

Snap
  • 492
  • 5
  • 14