Тестирование консольных приложений/программ-Java

все,

Я написал приложение телефонной книги на 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 ответов


почему бы не написать заявление, чтобы взять 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");

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

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



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

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

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

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

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

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

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

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

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

держите его простым, он окупается.


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

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

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

для деталей взгляните на документация по системным правилам.


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

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

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

}