1

我需要在我的程序中测试来自控制台的输入:

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- 从输入中读取整数。

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- 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"));
    }
}

当我运行测试时,我得到异常:java.util.NoSuchElementException: No line found.

如果我尝试divisor在线读取第二个数字 ( ),则会引发此异常:

final String inputString = scanner.nextLine();

如何修复测试,以便不会发生异常?

4

2 回答 2

1

您不想调用scanner.close();,因为Scanner.close()会在这里关闭底层Closable实例,System.in因此您的虚拟测试输入流将被丢弃。
此外,即使通过删除显式关闭操作,它仍然会在运行时失败:

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);
}

因为方法 return 将使扫描仪有资格成为 GC,因此System.in仍将关闭。你想要的是System.in.close()不被调用。
您有两种简单的方法:防止从扫描仪实例或直接从底层资源实例(System.in)进行此调用。你可以System.inFilterInputStream.

例如 :

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

    }
}

然后修改要测试的方法,让它接受这个对象。

于 2018-11-12T13:19:12.130 回答
1

我会以接受扫描仪的方式设计 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);
    }
}

主类如下所示:

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);
    }
}

然后测试看起来如下:

// 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);
    }
}

这样扫描仪就不会关闭。请注意,测试不是绿色的。我用的是JUnit,不知道你用的是什么技术。

请注意:

  1. 如果我设计一个类,我不会让它依赖于 System.*,而是给它一些流来使用。这简化了测试。

  2. 如果您在代码中的任何地方使用new,您需要知道该类是否需要执行此操作,或者如果其他人、构建器或工厂应该创建此实例。

于 2018-11-12T13:31:54.783 回答