Как я могу протестировать несколько входов с консоли?

Мне нужно проверить ввод с консоли в моей программе:

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

Когда я запускаю тест, я получаю исключение: java.util.NoSuchElementException: No line found .

Это исключение бросается, если я пытаюсь прочитать второе число ( divisor ) в строке:

final String inputString = scanner.nextLine();

Как я могу исправить тест, чтобы исключение не произошло?

Всего 2 ответа


Я бы разработал 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
3
".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
3", actual);
    }
}

Таким образом, сканер не закрыт. Обратите внимание, что тест не зеленый. Я использовал JUnit, я не знаю, какую технологию вы использовали.

Пожалуйста, обратите внимание:

  1. Если я создаю класс, я бы не стал зависеть от System. *, Но дайте ему некоторый поток для работы. Это облегчает тестирование.

  2. если вы используете новое в любом месте своего кода, вам нужно знать, имеет ли этот класс это желание или кто-то другой, строитель или фабрика должны скорее создать этот экземпляр.


Вы не хотите вызывать 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);
}

потому что возврат метода сделает сканер подходящим для GC, и поэтому System.in все равно будет закрыт. Вы хотите, чтобы System.in.close() не вызывался.
У вас есть два простых способа: предотвратить этот вызов из экземпляра сканера или из основного экземпляра ресурса напрямую ( System.in ). Вы можете украсить System.in с помощью FilterInputStream .

Например :

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

    }
}

Затем измените метод проверки, чтобы он принял этот объект.


Есть идеи?

10000