JUnit test for console input and output
Asked Answered
C

2

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);
    }
}
Charpoy answered 8/9, 2012 at 14:40 Comment(1)
I wanna learn how to work with itCharpoy
P
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.

Perice answered 8/9, 2012 at 14:45 Comment(4)
How can I use this to have for example, multiple inputs corresponding to multiple prompts for information?Dwarfish
I have the same question. Let me know if you found something...!Floatplane
@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.Perice
Thanks. But I used the System Rules library stefanbirkner.github.io/system-rulesFloatplane
F
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.

Fencer answered 8/9, 2020 at 23:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.