JUnit test for console input and output

前端 未结 2 481
说谎
说谎 2020-12-21 13:19

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...

相关标签:
2条回答
  • 2020-12-21 13:48

    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.

    0 讨论(0)
  • 2020-12-21 14:06

    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.

    0 讨论(0)
提交回复
热议问题