Мне нужно проверить ввод с консоли в моей программе:
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, я не знаю, какую технологию вы использовали.
Пожалуйста, обратите внимание:
Если я создаю класс, я бы не стал зависеть от System. *, Но дайте ему некоторый поток для работы. Это облегчает тестирование.
если вы используете новое в любом месте своего кода, вам нужно знать, имеет ли этот класс это желание или кто-то другой, строитель или фабрика должны скорее создать этот экземпляр.
Вы не хотите вызывать 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 {
}
}
Затем измените метод проверки, чтобы он принял этот объект.