Ложный ввод пользовательской консоли в тесте junit - PullRequest
0 голосов
/ 29 апреля 2018

Я пытаюсь смоделировать несколько пользовательских вводов через консоль, у меня возникла проблема при чтении «> 1» в качестве ввода. Чтение только с «1» кажется нормальным.

Учитывая следующий метод для тестирования:

public int getMainMenuSelectedOption() {
    Scanner scan = new Scanner(System.in);
    int i = scan.nextInt();
    while ((i < 1) || (i > 5)) {
      System.out.println("Invalid option, please select again");
      i = scan.nextInt();
    }
    return i;
}

Я написал тест как таковой

private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
private final ByteArrayOutputStream errContent = new ByteArrayOutputStream();

@Before
public void setUpStreams() {
    System.setOut(new PrintStream(outContent));
    System.setErr(new PrintStream(errContent));
}

@Test
public void testGetMainMenuSelectedOptionNotifyInvalidOption() {
    ByteArrayInputStream in = new ByteArrayInputStream("0".getBytes());
    System.setIn(in);
    app.getMainMenuSelectedOption();
    assertEquals("Invalid option, please select again", outContent.toString());
    ByteArrayInputStream in2 = new ByteArrayInputStream("1".getBytes());
    System.setIn(in2);
}

// Also tried with SequenceInputStream. This will not get the expected outstream of "Invalid option".
@Test
public void testGetMainMenuSelectedOptionNotifyInvalidOption() {
    ByteArrayInputStream in1 = new ByteArrayInputStream("0".getBytes());
    ByteArrayInputStream in2 = new ByteArrayInputStream("1".getBytes());
    SequenceInputStream in = new SequenceInputStream(in1,in2);
    System.setIn(in);
    app.getMainMenuSelectedOption();
    assertEquals("Invalid option, please select again\n", outContent.toString());
}

// This test runs fine
@Test
public void testGetMainMenuSelectedValidOption() {
    ByteArrayInputStream in = new ByteArrayInputStream("1".getBytes());
    System.setIn(in);
    app.getMainMenuSelectedOption();   
}

Я получил следующую ошибку

java.util.NoSuchElementException
    at java.util.Scanner.throwFor(Scanner.java:862)
    at java.util.Scanner.next(Scanner.java:1485)
    at java.util.Scanner.nextInt(Scanner.java:2117)
    at java.util.Scanner.nextInt(Scanner.java:2076)
    at com.twu.biblioteca.BibliotecaApp.getMainMenuSelectedOption(BibliotecaApp.java:42)
    at com.twu.biblioteca.ExampleTest.testGetMainMenuSelectedOptionNotifyInvalidOption(ExampleTest.java:73)

Ответы [ 3 ]

0 голосов
/ 29 апреля 2018

Я бы предложил вам принять новый метод обработки вашего теста.

Реализация собственного ByteArrayInputStream -> просто расширить существующий ByteArrayInputStream в свой собственный MyTestByteArrayInputStream.

Рассмотрим следующий пример:

import java.io.ByteArrayInputStream;
import java.util.stream.*;

public class MyClass {

    public static class MyTestByteArrayInputStream extends ByteArrayInputStream {
        private Byte mockedBuffer = null;

        public MyTestByteArrayInputStream(byte b) {
            super(new byte[] {b});
            this.mockedBuffer = b;
        }

        public MyTestByteArrayInputStream(byte[] in) {
            super(in);
        }
        public MyTestByteArrayInputStream(byte[] in, int offset, int length) {
            super(in,offset,length);
        }

        @Override
        public int read() {
            if(mockedBuffer == null) {
                return super.read();
            }
            else {
                return mockedBuffer;
            }
        }
    }

    public static void main(String args[]) {
        byte b = 49;
        MyTestByteArrayInputStream myTestInputStream = new MyTestByteArrayInputStream(b);
        IntStream.range(0,10).forEach(i -> {
                System.out.println("Invoking #" + i +": " + myTestInputStream.read()); 
            });
    }
}

Выход:

Invoking #0: 49
Invoking #1: 49
Invoking #2: 49
Invoking #3: 49
Invoking #4: 49
Invoking #5: 49
Invoking #6: 49
Invoking #7: 49
Invoking #8: 49
Invoking #9: 49

У вас много универсальности с вашей имитационной реализацией.

В моем примере я добавил конструктор, который позволяет создавать экземпляры IS одним байтом, который позже будет единственным выходом переопределенного метода read ().

Это означает, что вам не нужно «подавать» входной поток входными данными - вызов метода read () никогда не вызовет NoSuchElementException.

Я также настоятельно рекомендую использовать фактическую среду для насмешек: JMock и Mockito очень хорошо известны и полезны для широкого применения в модульном тестировании.

Использование моделирующей среды избавит вас от необходимости фактически реализовывать MyTestByteArrayInputStream - вместо этого вы просто переопределите поведение определенного экземпляра ByteArrayInputStream.

Надеюсь, что это поможет, и не стесняйтесь спрашивать / комментировать об этом решении.

0 голосов
/ 29 апреля 2018

Я решил, что для настройки последовательного потока ввода с консоли просто добавьте "\ n" как перевод строки.

Например, я хотел «0», а затем «1» консольный ввод, поэтому будет работать следующее

    ByteArrayInputStream in = new ByteArrayInputStream("0\n1\n".getBytes());
    System.setIn(in);
    app.getMainMenuSelectedOption();
    assertEquals("Invalid option, please select again\n", outContent.toString());
0 голосов
/ 29 апреля 2018

В вашем методе:

   Scanner scan = new Scanner(System.in);
   int i = scan.nextInt();
   while ((i < 1) || (i > 5)) {
        System.out.println("Invalid option, please select again");
        i = scan.nextInt();
    }
    return i;

для ввода 0 читает элемент от 0 до i, затем входит в цикл (потому что условие i<1), затем печатает сообщение, затем пытается сканировать следующий элемент в цикле. Поскольку входной поток уже исчерпан согласно документации Scanner # nextInt () :

Выдает:
NoSuchElementException - если ввод исчерпан

На мой взгляд, все в порядке (кроме ошибки в логике вашего метода).

---------- РЕДАКТИРОВАТЬ ----------

Допустимые значения должны быть от 1 до 4, оба включительно. Почему вход поток исчерпан?

Я не знаю, почему вы не используете отладчик для проверки того, что происходит в вашем коде, просто нажмите на строку в правой панели в вашей IDE, чтобы установить точку останова, затем щелкните правой кнопкой мыши свой метод тестирования и выберите « отладка "из контекстного меню.

В любом случае, давайте отладим ваш метод для ввода 0 шаг за шагом на бумаге:

1   Scanner scan = new Scanner(System.in);
2   int i = scan.nextInt();
3   while ((i < 1) || (i > 5)) {
4        System.out.println("Invalid option, please select again");
5        i = scan.nextInt();
6    }
7    return i;

Входной буфер содержит один символ 0.

  • Поток выполнения перемещается в строку 2
  • В строке 2 сканер считывает символ 0 из входного потока в переменную i. Переменная i теперь содержит значение 0. После этого входной поток пуст (требуется)
  • в строке 3 проверяется условие ((i < 1) || (i > 5)). Поскольку i равно 0, условие выполняется, и поток программы входит в цикл
  • в строке 4 сообщение выводится на консоль
  • в строке 5 сканер пытается прочитать следующее целое число из входного буфера. Поскольку входной буфер исчерпан, генерируется исключение

Думаю, теперь все должно быть ясно. Почему входной поток исчерпан? Поскольку у него был только один элемент 0, который был прочитан в строке 2, то в строке 5 он был пуст (исчерпан)
В вашем коде есть ошибка, но именно поэтому у нас есть модульный тест, чтобы найти такие ошибки).
Совет: используйте метод Scannes # hasNextInt , чтобы проверить, есть ли следующий int во входном потоке, чтобы избежать исключения.

...