Метод Mock OutputStream #flush

#java #unit-testing #mockito #java-io

#java #модульное тестирование #mockito #java-io

Вопрос:

У меня есть: у меня есть метод void метода класса service. Этот метод берет некоторые данные с удаленного и затем удаляет их OutputStream .

 public void pullAndFlushData(URI uri, Params params) {
  InputStream input = doHttpRequest(uri, params);
  OutputStream output = new OutputStream("somepath");
  IOUtils.copyLarge(input, output);
  output.flush();
  output.close();
}
  

Я хочу: протестировать результаты этого метода. Итак, я хочу издеваться output.flush() и проверять, содержит ли он правильные данные.

Вопрос: Как издеваться OutputStream#flush method ?

Комментарии:

1. Если вы принимаете an OutputStream в качестве аргумента вашего метода, вы можете протестировать метод, передав a ByteArrayOutputStream и протестировав содержимое. Я не вижу возможности для издевательства над вашим текущим кодом.

2. Примечание: ваш метод не делает ничего полезного. Я думаю, это потому, что вы упростили его для этого вопроса. Я думаю, что вы слишком упростили его, что затруднит нам советовать, как его протестировать.

3. @Duncan Я не принимаю OutputSteam в качестве аргумента свой метод. IOUtils является сторонним классом Google

4. @Duncan Он берет данные из какой-либо службы и сохраняет их в файлах. Да, он может изменять данные между этими двумя действиями. Но в данном случае это действительно не имеет значения

5. Я предполагал, что вы должны добавить аргумент. Иногда ваш код необходимо изменить, чтобы сделать его пригодным для тестирования.

Ответ №1:

Ваш текущий код не будет работать:

  OutputStream output = new OutputStream("SomePath");
  

… не будет компилироваться, потому что OutputStream является абстрактным.

Итак, где-то вам нужно будет указать методу, какой OutputStream использовать. Чтобы сделать его более тестируемым, сделайте поток параметром.

  public void pullAndFlushData(OutputStream output, URI uri, Params params) {
    InputStream input = doHttpRequest(uri, params);
    IOUtils.copyLarge(input, output);
    output.flush();
    output.close();
 }
  

Альтернативно, output это может быть поле в объекте, заполненное конструктором или установщиком. Или вы могли бы передать объекту фабрику. Какой бы из них вы ни выбрали, это означает, что вызывающая сторона может контролировать, какой тип OutputStream используется — для производственного кода, a FileOutputStream ; для тестов, a ByteArrayOutputStream .

Возможно, вы захотите пересмотреть решение для close() OutputStream здесь — и вместо этого сделать это в том же блоке, в котором открыт OutputStream .

Теперь вы можете протестировать его, предоставив свой модульный тест OutputStream.

 @Test
public void testPullAndFlushData() {
     URI uri = ...;
     Params params = ...;
     ByteArrayOutputStream baos = new ByteArrayOutputStream();
     someObject.pullAndFlushData(baos, uri, params);
     assertSomething(..., baos.toByteArray());
}
  

При этом не используется Mockito, но это хороший шаблон для тестирования методов, использующих OutputStream.

Вы могли бы позволить Mockito имитировать OutputStream и использовать его таким же образом — устанавливая ожидания для write() его вызовов. Но это стало бы довольно хрупким в отношении того, как copyLarge() фрагментируются данные.

Вы также можете использовать Mockito spy() , чтобы проверить, были ли сделаны вызовы вашему реальному потоку ByteArrayOutputStream.

 @Test
public void testPullAndFlushData() {
     URI uri = ...;
     Params params = ...;
     ByteArrayOutputStream spybaos = spy(new ByteArrayOutputStream());
     someObject.pullAndFlushData(spybaos, uri, params);
     assertSomething(..., spybaos.toByteArray());
     verify(spybaos).flush(); // asserts that flush() has been called.
}
  

Однако обратите внимание, что команда Mockito довольно неохотно предоставляла spy() и в большинстве случаев не считает, что это хороший способ тестирования. Прочитайте документы Mockito по причинам.

Комментарии:

1. 1 Это именно то, о чем я думал. (Хотя я был ленив и написал комментарий, а не хороший ответ). Я бы почти подумал об удалении ваших последних нескольких абзацев — мы не хотим, чтобы новые пользователи думали, что это хороший способ тестирования.

2. @Duncan он специально хотел проверить flush(). Я собираюсь упомянуть, что команда Mockito не одобряет.