Тестирование консольных приложений / программ - Java - PullRequest
12 голосов
/ 20 ноября 2010

Все,

Я написал приложение PhoneBook на Java, основанное на командной строке.Приложение в основном запрашивает некоторые данные пользователя, такие как имя, возраст, адрес и номера телефонов, и сохраняет их в файле.Другие операции включают поиск телефонной книги по имени, номеру телефона и т. Д. Все данные вводятся через консоль.

Я пытаюсь написать контрольные примеры JUnit для каждой из реализованных мной функций, но не могу разобратьсякак перенаправить System.in в коде реализации на что-то в моих тестовых методах JUnit, которые будут предоставлять эти значения, когда мой фактический код останавливается для пользовательского ввода?

Пример:

Мой код реализации имеет:

BufferedReader is = new BufferedReader (new InputStreamReader(System.in));
System.out.println("Please enter your name:");
String name = is.readLine();             // My test cases stop at this line. How can I pass command line values i.e. redirect System.in to my test based values?

Надеюсь, это имеет смысл

Ответы [ 5 ]

11 голосов
/ 20 ноября 2010

Почему бы не написать ваше приложение, чтобы принять Reader в качестве ввода?Таким образом, вы можете легко заменить InputStreamReader(System.in) на FileReader(testFile)

public class Processor {
    void processInput(Reader r){ ... }
}

И затем два экземпляра:

Processor live = new Processor(new InputStreamReader(System.in));
Processor test = new Processor(new FileReader("C:/tmp/tests.txt");

Привыкание к кодированию интерфейса принесет большие преимуществапочти во всех аспектах ваших программ!

Также обратите внимание, что Reader - это идиоматический способ обработки ввода на основе символов в программах Java.InputStream s следует зарезервировать для необработанной обработки на уровне байтов.

4 голосов
/ 20 ноября 2010
3 голосов
/ 20 ноября 2010

Я предлагаю вам разделить код на три части:

  • Чтение ввода (например, name в вашем примере)
  • Делайте то, что вам нужно сделать с этим входом
  • Распечатать результаты

Вам не нужно тестировать ввод и результаты печати, поскольку это код Java, который уже тестировался людьми, пишущими Java.

Единственное, что вам нужно проверить, это то, что вы делаете, что бы это ни было. Модульные тесты названы так, потому что они тестируют блоки кода изолированно. Вы не тестируете всю программу, вы тестируете небольшие фрагменты, которые являются автономными и имеют четко определенную функцию.

В модульных тестах не следует полагаться на операции ввода / вывода. Вы должны предоставить входы и ожидаемые результаты непосредственно в модульном тесте. Иногда удобно использовать операции чтения файлов для ввода или вывода (например, если объем данных огромен), но, как правило, чем больше вы вводите / выводите в своих модульных тестах, тем сложнее они становятся, и вы скорее не делать юнит, а интеграционные тесты.

В вашем случае вы как-то используете name. Если это единственный параметр, тогда создайте метод - давайте назовем его nameConsumer - который принимает это имя, что-то делает и возвращает его результат. В своих юнит-тестах сделайте что-то вроде этого:

@Test
public void testNameConsumer() {
    // Prepare inputs
    String name = "Jon";
    String result = nameConsumer(name);
    assertEquals("Doe", result);
}

Перенесите ваши println и readLine вызовы на другие методы и используйте около nameConsumer, но не в своих модульных тестах.

Подробнее об этом здесь:

Проще говоря, это окупается.

2 голосов
/ 29 июня 2013

Библиотека Системные правила предоставляет правило TextFromStandardInputStream для имитации ввода в тестах JUnit.

public class YourAppTest {
  @Rule
  public TextFromStandardInputStream systemInMock = emptyStandardInputStream();

  @Test
  public void test() {
    systemInMock.provideText("name\nsomething else\n");
    YourApp.main();
    //assertSomething
  }
}

Подробнее см. В СистемаПравила документации .

1 голос
/ 13 октября 2015

Это занимает базовое консольное приложение циклического воспроизведения и делает его тестируемым, используя идеи oxbow_lakes 'answer .

Правильный класс:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;

public class TestableLoopingConsoleExample {

   public static final String INPUT_LINE_PREFIX = "> ";
   public static final String EXIT_COMMAND = "exit";
   public static final String RESPONSE_PLACEHOLDER = "...response goes here...";
   public static final String EXIT_RESPONSE = "Exiting.";

   public static void main(String[] cmdLineParams_ignored) throws IOException {
      BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
      PrintStream out = new PrintStream(System.out);
      PrintStream err = new PrintStream(System.err);

      try {
         new TestableLoopingConsoleExample().main(cmdLineParams_ignored, in, out);
      } catch (Exception e) {  //For real use, catch only the exactly expected types
         err.println(e.toString());
      }
   }

... продолжение ...

   public void main(String[] cmdLineParams_ignored, BufferedReader in, PrintStream out)
         throws IOException {

      System.out.println("Enter some text, or '" + EXIT_COMMAND + "' to quit");

      while (true) {

         out.print(INPUT_LINE_PREFIX);
         String input = in.readLine();
         out.println(input);

         if (input.length() == EXIT_COMMAND.length() &&
            input.toLowerCase().equals(EXIT_COMMAND)) {

            out.println(EXIT_RESPONSE);
            return;
         }

         out.println(RESPONSE_PLACEHOLDER);
      }
   }
}

Тест (JUnit4):

import static org.junit.Assert.assertEquals;
import static testableloopingconsoleapp.TestableLoopingConsoleExample.EXIT_COMMAND;
import static testableloopingconsoleapp.TestableLoopingConsoleExample.EXIT_RESPONSE;
import static testableloopingconsoleapp.TestableLoopingConsoleExample.INPUT_LINE_PREFIX;
import static testableloopingconsoleapp.TestableLoopingConsoleExample.RESPONSE_PLACEHOLDER; 

import org.junit.Before;
import org.junit.Test; 

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.StringReader; 

public class TestableLoopingConsoleExampleTest { 

  private final ByteArrayOutputStream out = new ByteArrayOutputStream();
  private final ByteArrayOutputStream err = new ByteArrayOutputStream(); 

  @Before
  public final void resetOutputStreams() {
     out.reset();
     err.reset();
  } 

... продолжение ...

  @Test
  public void testableMain_validInputFromString_outputAsExpected() throws Exception {
     String line1 = "input line 1\n";
     String line2 = "input line 2\n";
     String line3 = "input line 3\n";
     String exitLine = EXIT_COMMAND + "\n"; 

     BufferedReader in = new BufferedReader(new StringReader(
         line1 + line2 + line3 + exitLine
     ));
     String expectedOutput =
         INPUT_LINE_PREFIX + line1 +
         RESPONSE_PLACEHOLDER + "\n" +
         INPUT_LINE_PREFIX + line2 +
         RESPONSE_PLACEHOLDER + "\n" +
         INPUT_LINE_PREFIX + line3 +
         RESPONSE_PLACEHOLDER + "\n" +
         INPUT_LINE_PREFIX + exitLine +
         EXIT_RESPONSE + "\n"; 

     String[] ignoredCommandLineParams = null; 

     new TestableLoopingConsoleExample().main(ignoredCommandLineParams, in, new PrintStream(out)); 

     assertEquals(expectedOutput, out.toString());
  } 

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...