简体   繁体   English

如何从控制台测试多个输入?

[英]How can I test multiple inputs from console?

I need to test input from console in my program: 我需要在程序中测试来自控制台的输入:

Main.java - class with main logic. Main.java具有主逻辑的类。

public class Main {
    public static void main(final String[] args) {
        final Integer dividend = new IntegerReader().fetchIntegerNumber("Input dividend: ");
        final Integer divisor = new IntegerReader().fetchIntegerNumber("Input divisor: ");
        System.out.println(dividend);
        System.out.println(divisor);
    }
}

IntegerReader.java - reads integer number from input. IntegerReader.java从输入中读取整数。

import java.util.Scanner;

public class IntegerReader {

    public Integer fetchIntegerNumber(final String message) {
        System.out.print(message);
        final Scanner scanner = new Scanner(System.in);
        final String inputString = scanner.nextLine();
        scanner.close();
        return Integer.valueOf(inputString);
    }
}

MainTest.java - test of Main.java class. MainTest.java测试Main.java类。

class MainTest {
    private final InputStream testInputStream = new ByteArrayInputStream("2\n3".getBytes());
    private final ByteArrayOutputStream testOutputStream = new ByteArrayOutputStream();
    private InputStream initialInputStream = System.in;
    private PrintStream initialOutputStream = System.out;

    @BeforeEach
    void setUpStreams() {
        System.setIn(testInputStream);
        System.setOut(new PrintStream(testOutputStream));
    }

    @AfterEach
    void restoreDefaultStreams() {
        System.setIn(initialInputStream);
        System.setOut(initialOutputStream);
        System.out.println("Test initial output stream");
    }

    @Test
    void fetchesTextFromInput() {
        Main.main(new String[]{});
        final String actual = testOutputStream.toString();
        assertThat(actual, is("Input dividend: Input divisor: 2\n3"));
    }
}

When I run test, I get exception: java.util.NoSuchElementException: No line found . 运行测试时,出现异常: java.util.NoSuchElementException: No line found

This exception is thrown, if I try to read second number ( divisor ) on line: 如果我尝试在线读取第二个数字( divisor ),则会引发此异常:

final String inputString = scanner.nextLine();

How can I fix test, so that exception will not occur? 如何修复测试,以便不会发生异常?

You don't want to invoke scanner.close(); 您不想调用scanner.close(); because Scanner.close() will close the underlying Closable instance, here System.in and so your dummy test inputstream will be discarded. 因为Scanner.close()将关闭基础的Closable实例(此处为System.in ,所以您的虚拟测试输入流将被丢弃。
Besides even by removing the explicit closing operation it will still fails at runtime : 除了删除显式关闭操作外,它在运行时仍将失败:

public Integer fetchIntegerNumber(final String message) {
    System.out.print(message);
    final Scanner scanner = new Scanner(System.in);
    final String inputString = scanner.nextLine();
    // REMOVED scanner.close();
    return Integer.valueOf(inputString);
}

because the method return will make the scanner eligible to be GC and so System.in will still be closed. 因为方法返回将使扫描仪有资格成为GC,因此System.in仍将关闭。 What you want is that System.in.close() be not invoked. 您想要的是不调用System.in.close()
You have two simple ways : Prevent this invocation from the scanner instance or from the underlying resource instance directly ( System.in ). 您有两种简单的方法:防止直接从扫描程序实例或基础资源实例( System.in )进行此调用。 You could decorate System.in with FilterInputStream . 您可以使用FilterInputStream装饰System.in

For example : 例如 :

InputStream inWithoutClose = new FilterInputStream(System.in) {
    @Override
    public void close() throws IOException {

    }
}

Then modify the method to test to make it accept this object. 然后修改方法进行测试以使其接受该对象。

I would design the IntegerReader in the way that it accepts a scanner: 我将以接受扫描仪的方式设计IntegerReader:

public class IntegerReader {

    private final Scanner scanner;

    public IntegerReader(Scanner theScanner) {
        this.scanner = theScanner;
    }

    public Integer fetchIntegerNumber(final String message) {
        System.out.print(message);
        final String inputString = scanner.nextLine();
        return Integer.valueOf(inputString);
    }
}

The main class would look like this: 主类如下所示:

public class Main {
    private Scanner scanner;

    public Main(Scanner theScanner) {
        this.scanner = theScanner;
    }
    public static void main(final String[] args) {
        new Main(new Scanner(System.in)).process();
    }

    public void process() {
        final Integer dividend = new IntegerReader(scanner).fetchIntegerNumber("Input dividend: ");
        final Integer divisor = new IntegerReader(scanner).fetchIntegerNumber("Input divisor: ");
        System.out.println(dividend);
        System.out.println(divisor);
    }
}

The test then looks the following way: 然后,测试看起来如下:

// this test still fails, but. ..
public class MainTest {
private final InputStream testInputStream = new ByteArrayInputStream("2\n3\n".getBytes());
    private final ByteArrayOutputStream testOutputStream = new ByteArrayOutputStream();
    private PrintStream initialOutputStream = System.out;

    @Before
    public void setUpStreams() {
        System.setOut(new PrintStream(testOutputStream));
    }

    @After
    public void restoreDefaultStreams() {
        System.setOut(initialOutputStream);
        System.out.println("Test initial output stream");
    }

    @Test
    public void fetchesTextFromInput() {
        Scanner scanner = new Scanner(testInputStream);
        Main main = new Main(scanner);
        main.process();
        final String actual = testOutputStream.toString();
        Assert.assertEquals("Input dividend: Input divisor: 2\n3", actual);
    }
}

In this way the scanner is not closed. 这样,扫描仪不会关闭。 Note that the test is not green. 请注意,测试不是绿色的。 I used JUnit, I don't know which technology you used. 我使用过JUnit,但不知道您使用了哪种技术。

Please note: 请注意:

  1. If I design a class I would not make it dependend on System.*, but give it some stream to work with. 如果设计一个类,则不会使它依赖于System。*,而是给它一些工作流。 This eases testing. 这简化了测试。

  2. if you use new anywhere in your code you need to know whether the class has the concern to do this or if someone else, a builder or factory should rather create this instance. 如果您在代码中的任何地方使用new ,则需要知道该类是否需要执行此操作,或者是否需要其他人,则应该由构建器或工厂创建此实例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM