2010-11-19 4 views
9

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

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

Я пытаюсь написать тестовые примеры 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? 

Надеется, что это имеет смысл

+0

Вы можете найти пример кода, чтобы сделать то, что вы здесь описываете: http://illegalargumentexception.blogspot.com/2010/09/java-systemconsole-ides-and-testing.html Но это в значительной степени просто способ реализовать то, что oxbow_lakes описывает в своем ответе. – McDowell

ответ

11

Почему бы не написать приложение, чтобы взять 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 является Идиоматический способ обработки ввода на основе символов в программах Java. InputStream s должны быть зарезервированы для обработки необработанного байтового уровня.

+0

Я тебя не понял. Как код, описанный выше, работает в моем примере? Поэтому, если мне нужно передать значения командной строки в свой тестовый метод, я должен использовать файл 'tests.txt'? Если это так, я хочу передать значения через свой тестовый метод, а не создавать файлы, которые просто передают значения. –

+0

Два 'Reader' имеют один и тот же интерфейс. Вы используете «FileReader» для ваших модульных тестов, потому что он не требует ввода пользователем (вы готовите текстовые файлы для тестирования различных входов). Вы используете 'InputStreamReader (System.in)' для того, когда вы выполняете программу вживую, поэтому она будет по-прежнему запрашивать ввод пользователя напрямую. – Santa

3
+0

Не могли бы вы рассказать о своем ответе? Почему я должен использовать файл «input.txt»? –

+1

@ darkie15: Это просто пример. – thejh

+0

@ darkie15: Вы также можете работать с 'PipedInputStream' и' PipedOutputStream' или так. – thejh

3

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

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

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

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

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

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

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

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

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

Держите это просто, это окупается.

2

В библиотеке System Rules приведено правило TextFromStandardInputStream для имитации ввода в тестах JUnit.

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

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

Подробнее см. На System Rules documentation.

+0

Очень полезная библиотека! Использовал его здесь: https://github.com/binwiederhier/syncany/blob/7ee6174877a646c78e2f5faa36115d01bf6cc9ec/syncany-cli/src/test/java/org/syncany/tests/cli/InitCommandTest.java#L137 – binwiederhier

1

Это берет basic looping console application и делает его пригодным для тестирования, используя идеи от 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()); 
    } 

} 
+0

Phwoaaa .... что не так красиво, как некоторые тесты могут быть. Упрощенный tho, thx! – Crowie

Смежные вопросы