#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
в качестве аргумента вашего метода, вы можете протестировать метод, передав aByteArrayOutputStream
и протестировав содержимое. Я не вижу возможности для издевательства над вашим текущим кодом.2. Примечание: ваш метод не делает ничего полезного. Я думаю, это потому, что вы упростили его для этого вопроса. Я думаю, что вы слишком упростили его, что затруднит нам советовать, как его протестировать.
3. @Duncan Я не принимаю
OutputSteam
в качестве аргумента свой метод.IOUtils
является сторонним классом Google4. @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 не одобряет.