2016-09-22 3 views
0

Это не то же самое, что и этот вопрос: JUnit: How to simulate System.in testing?, что касается насмешливого stdin.Как я могу проверить, что приложение CLI ждет ввода

Я хочу знать, как проверить (как в TDD), что простой метод Java с методом main ждет ввода.

Мой тест:

@Test 
public void appRunShouldWaitForInput(){ 
    long startMillis = System.currentTimeMillis(); 
    // NB obviously you'd want to run this next line in a separate thread with some sort of timeout mechanism... 
    // that's an implementation detail I've omitted for the sake of avoiding clutter! 
    App.main(null); 
    long endMillis = System.currentTimeMillis(); 
    assertThat(endMillis - startMillis).isGreaterThan(1000L); 
} 

Мой SUT main:

public static void main(String args[]) { 
    BufferedReader br = null; 
    try { 
     br = new BufferedReader(new InputStreamReader(System.in)); 
     System.out.print("Enter something : "); 
     String input = br.readLine(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 

... тест не пройден. Код не ждет. Но когда вы запускаете приложение в командной строке, он действительно ждет.

NB кстати я также попробовать с установкой stdin в sthg еще:

System.setIn(new ByteArrayInputStream(dummy.getBytes())); 
scanner = new Scanner(System.in); 

... это не выдержал испытание.

+0

Если * сделали * ожидание ввода, ваше испытание будет ждать вечно. Кроме того, вы действительно думаете, что это хороший тест? Он не тестирует * ваш * код, это тестирование 'BufferedReader'. – Kayaman

+0

Если вы действительно хотите протестировать 'BufferedReader', то сообщение, которое вы связали, - это то, что вам нужно. Просто помните, что идея TDD или любого тестирования заключается не в том, чтобы проверить код, который был разработан (и, надеюсь, проверен) другими. Тестирование того, блокирует ли 'readLine()' до тех пор, пока не получит '\ n', совершенно откровенно смешно. – Kayaman

+0

(слегка измененный, чтобы показать, что я пытаюсь сделать). Я не думаю, что речь идет о тестировании «BufferedReader». Метод 'main' без строки' br.readLine() 'немедленно возвращается: поэтому мне нужен тест, чтобы оправдать включение этой строки. Если эта строка кода приложения была впоследствии удалена или не выполнена по какой-то причине, я бы хотел, чтобы этот тест завершился неудачей. Простой факт заключается в том, что 'br.readLine()' определенно НЕ блокируется, когда класс приложения запускается из моего тестового метода. Почему нет? –

ответ

1

Как правило, статические методы (такие как методы main) трудно проверить. По этой причине вы почти никогда не вызываете метод main (или любой другой статический метод) из вашего тестового кода. Общий шаблон для работы вокруг этого, чтобы преобразовать это:

public class App { 
    public static void main(String args[]) { 
     BufferedReader br = null; 
     try { 
      br = new BufferedReader(new InputStreamReader(System.in)); 
      System.out.print("Enter something : "); 
      String input = br.readLine(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

к этому:

public class App { 
    private final InputStream input; 
    private final OutputStream output; 

    public App(InputStream input, OutputStream output) { 
     this.input = input; 
     this.output = output; 
    } 

    public static void main(String[] args) { 
     new App(System.in, System.out).start(); 
    } 

    public void start() { 
     BufferedReader br = null; 
     try { 
      br = new BufferedReader(new InputStreamReader(input)); 
      output.print("Enter something : "); 
      String nextInput = br.readLine(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

Теперь ваш тест становится этим:

@Test 
public void appRunShouldWaitForInput(){ 
    ByteArrayOutputStream output = new ByteArrayOutputStream(); 

    // As you have already noted, you would need to kick this off on another thread and use a blocking implementation of InputStream to test what you want to test. 
    new App(new ByteArrayInputStream(), output).start(); 

    assertThat(output.toByteArray().length, is(0)); 
} 

Ключевая идея заключается в том, что, когда ты запустите приложение «для реального», то есть с помощью метода main, он будет использовать стандартные входные и выходные потоки. Однако, когда вы запускаете его из своих тестов, он использует чисто входной/выходной поток в памяти, который вы полностью контролируете в своем тесте. ByteArrayOutputStream - всего лишь один пример, но в моем примере вы можете увидеть, что тест способен проверять фактические байты, которые были записаны в выходной поток.

+0

Спасибо - это очень понятно и полезно –

+0

Ваш ответ научил меня много ... хотя, наконец, я думаю, что я пришел к выводу, что эта блокировка System.in (завернутая в BufferedReader) 1) была широко протестирована и должны быть доверены и 2) на самом деле не могут быть протестированы в тестовом коде, если вы не используете System.in. Я борюсь TDD newb ... :) –

+0

У вас есть это место. Он не может быть протестирован в модульном тесте, но вы * можете * написать более крупный масштаб «Приемочный тест», который фактически запустит вашу программу в другом процессе и взаимодействует с ней через std in и std err, но это будет довольно громоздко, и в целом вы должны иметь очень небольшое количество этого стиля теста по сравнению с чистым модульным тестом. – tonicsoft

1

в общем, вы не можете проверить, если ваша программа ждет, пока что-то не происходит (потому что вы не можете проверить, если он ждет навсегда)

, что обычно делается в таких случаях:

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

  2. в общем случае это тестирование параллельного программирования. это сложно, поэтому ppl обычно пытается найти какое-то полезное упрощение:

    1. Вы можете просто проверить, правильно ли вывод вашей программы на вход, предоставленный в тестах.
    2. для некоторых действительно сложных проблем выше техники сочетается с запуском теста в тысячу раз (и, если возможно, для принудительного переключения потоков) для обнаружения ошибок в параллельном программировании (нарушенных инвариантов)
    3. перед тем, как ваш тест предоставит дату ввода, вы можете проверить, ваша программа находится в правильном состоянии (ожидающий поток, правильные значения полей и т. д.)
    4. В худшем случае вы можете использовать задержку в тестах перед тем, как обеспечить ввод, чтобы убедиться, что ваша программа ждет и использует вход. этот метод часто используется, потому что это просто, но это вонючее и если добавить больше тестов, как, что весь ваш костюм становится медленнее
+0

спасибо - полезно –

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