javastringunit-testingtestingbytearrayinputstream

How to take predefined user input for unit tests in Java?


I am trying to use the same input from the unit test throughout the entire program, but the program has different scanner instances across its functions and classes and the input stream withdrawn from the string only works at the very first scanner but not the rest.

import java.io.ByteArrayInputStream;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        String data = "1\nThis is the first input\nThis is the second input";

        ByteArrayInputStream is = new ByteArrayInputStream(data.getBytes());
        System.setIn(is);
        
        Scanner scanner = new Scanner(System.in);
        System.out.println(scanner.nextLine());
        scanner.close();

        func();
    }

    private static void func() {
        Scanner scanner = new Scanner(System.in);

        System.out.println("First input: " + scanner.nextLine());
        System.out.println("Second input: " + scanner.nextLine());

        scanner.close();
    }
}

This is a representation of the issue I am facing, my real version has more complex foundation, but the end result is the same.

1
Exception in thread "main" java.util.NoSuchElementException: No line found
        at java.base/java.util.Scanner.nextLine(Scanner.java:1651)
        at Main.func(Main.java:21)
        at Main.main(Main.java:15)

Solution

  • try do design your system for testability: Don't instanciate a new Scanner everywhere you need it, but pass it into your functions as parameter. So for the tests you can either use a mocked scanner or prepare a Scanner with an InputStream that represents your test input. But you will have either to reset that InputStream or create a new one per test. – cyberbrain

    This is based on cyberbrain’s suggestion, passing the scanner as a parameter to every function that may require a scanner was the solution, now it is optimized for testability and we can use string input unit tests without any issues

    import java.io.ByteArrayInputStream;
    import java.util.Scanner;
    
    public class Main {
        public static void main(String[] args) {
            String data = "1\nThis is the first input\nThis is the second input";
    
            ByteArrayInputStream is = new ByteArrayInputStream(data.getBytes());
            System.setIn(is);
            
            Scanner scanner = new Scanner(System.in);
            System.out.println(scanner.nextLine());
    
            func(scanner);
    
            scanner.close();
        }
    
        private static void func(Scanner scanner) {
            System.out.println("First input: " + scanner.nextLine());
            System.out.println("Second input: " + scanner.nextLine());
        }
    }