控制台 I/O 重定向后清空 InputStream

Empty InputStream after console I/O redirection

我必须测试一个程序,用户必须在其中输入 3 个数字,然后在控制台输出中得到结果。

我编写了测试并重定向了 System.in 和 System.out。 我的测试用例如下所示:

private static ByteArrayOutputStream mockOut;

@Test
public void correct_input_test() {
    test("> x = 0, y = 0", 1, 0, 0);
    test("> x = -1, y = 0", 1, 0, -1);
}

这是预期的控制台输出作为第一个参数和 3 个 int args 来准备输入流。

test()方法如下:

private static void test(String expected, int ... args) {
    set(args);       // preparing inputstream and redirecting Sys.i/o
    run(expected);   // running program test after setting up
}

根据需要允许输入任意数量的整数。这些参数进一步传递给 set() 方法,我在其中创建输入流并重定向 System.in set()方法:

private static void set(int... args) {
    String s = Arrays.stream(args)
               .mapToObj(String::valueOf)
               .collect(Collectors.joining(" "));          // assembling args String
    System.setIn(new ByteArrayInputStream(s.getBytes()));  // redirecting Sys.in 
    mockOut = new ByteArrayOutputStream();
    PrintStream stream = new PrintStream(mockOut);
    System.setOut(stream);                                 // redirecting Sys.out
}

传递的参数 1, 0, 0 变成了 String s = "1 0 0"

最后在设置 运行 调用我的程序的方法后

private static void run(String expected) {
    Main.main(null);                                              // call program to catch result
    String[] consoleLines = mockOut.toString().split("\n");       // split console output
    assertEquals(expected, consoleLines[consoleLines.length-1]);  // compare lines
}

在这里,我将在 mockOut 对象中获得的控制台输出分成不同的行,并将最后一行与预期的字符串进行比较。

仅当我测试一个测试用例时它才有效(如果我注释掉这些测试用例中的任何一个)。

@Test
public void correct_input_test() {
//    test("> x = 0, y = 0", 1, 0, 0);
    test("> x = -1, y = 0", 1, 0, -1);
}

但是,如果我在第二次尝试调用程序后尝试 运行,它会得到一个带有 java.util.NoSuchElementException 的空输入流,而我实际上并没有不知道为什么。 我试过关闭流,但我想这没关系。每次调用 set() 时,我都会使用非空参数字符串创建新的 InputStream。 我还尝试恢复默认 System.in 并在 set() 中再次重定向它 - 没有效果。

谁能解释一下我到底忘记了什么?

更新。我将我的代码分成方法来解释哪个是哪个,但以防它会增加可读性——下面是完全相同的代码:

private static ByteArrayOutputStream mockOut;

@Test
public void correct_input_test() {
    test("> x = 0, y = 0", 1, 0, 0);
    test("> x = -1, y = 0", 1, 0, -1);
}

private static void test(String expected, int ... args) {
    set(args);       // preparing inputstream and redirecting Sys.i/o
    run(expected);   // running program test after setting up
}

private static void set(int... args) {
    String s = Arrays.stream(args)
               .mapToObj(String::valueOf)
               .collect(Collectors.joining(" "));          // assembling args String
    System.setIn(new ByteArrayInputStream(s.getBytes()));  // redirecting Sys.in 
    mockOut = new ByteArrayOutputStream();
    PrintStream stream = new PrintStream(mockOut);
    System.setOut(stream);                                 // redirecting Sys.out
}

private static void run(String expected) {
    Main.main(null);                                              // call program to catch result
    String[] consoleLines = mockOut.toString().split("\n");       // split console output
    assertEquals(expected, consoleLines[consoleLines.length-1]);  // compare lines
}

更新。 这是堆栈跟踪:

java.util.NoSuchElementException: 

Arguments input incomplete.

    at Main.in(Main.java:169)
    at Main.main(Main.java:19)
    at MainTest$MainMethodTest.run(MainTest.java:75)
    at MainTest$MainMethodTest.test(MainTest.java:71)
    at MainTest$MainMethodTest$SwapMethodTest.correct_input_test(MainTest.java:29)

我知道异常的确切来源。 这是从 main() 调用的 in() 方法,用于获取用户的输入并在 Scanner 尝试读取 时捕获异常next() 来自输入流:

public static int in(String message, int from, int to) {
    if (message != null && !message.isEmpty()) {
        System.out.print(message);
    }
    try {
        String input = scanner.next();
        int num;

        /*         this code works correctly for me
                   and is just to check 
                   if user typed correct number 
                   and if the number is in range.
        try {
            if ((num = Integer.parseInt(input)) < from) {
                throw new IllegalArgumentException("\n\nExpected: value >= " + from + "\nActual:   value = " + num + "\n");
            } else if (num > to) {
                throw new IllegalArgumentException("\n\nExpected: value <= " + to + "\nActual:   value = " + num + "\n");
            } else {
                return num;
            }
        } catch (NumberFormatException e) {
            throw new NumberFormatException("\n\nExpected: integer number\nActual:   " + input + "\n");
        }
        */

    } catch (NoSuchElementException e) {
        throw new NoSuchElementException("\n\nArguments input incomplete.");
    }   // at Main.in(Main.java:169) - here is line 169
}

但是我不明白为什么IS是空的。

嗯。看来我解决了。

我刚刚将 Scanner 初始化从变量声明移到了 main()。

public class Main {
    private static Scanner scanner = new Scanner(System.in);

    public static void main(String[] args) {
        // ....
    }
}

现在对我有用了。

public class Main {
    private static Scanner scanner;

    public static void main(String[] args) {
        scanner = new Scanner(System.in);
        // ....
    }
}