Как я могу unit test закрыть этот входной поток?
У меня есть Runnable
по строкам:
public void run() {
InputStream inputStream = null;
try {
inputStream = new FileInputStream(file);
//more stuff here
}
catch (Exception e) {
//simplified for reading
}
finally {
if(inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {}
}
}
}
Как выполнить тест inputStream.close()
? В настоящее время я использую Mockito и JUnit. Я знаю, что инъекция inputStream
in - это идея, но я не хочу, чтобы ресурсы использовались до тех пор, пока не будет вызван run?()
, следовательно, это локальная переменная. Итак, как я могу перепроектировать мой код таким образом, чтобы я мог проверить, был ли вызван контакт?
Ответы
Ответ 1
Если я правильно понял задачу, это могло бы быть таким образом
static boolean isClosed;
public void run() {
InputStream inputStream = null;
try {
inputStream = new FileInputStream(file) {
@Override
public void close() throws IOException {
isClosed = true;
super.close();
}
};
// more stuff here
Ответ 2
Поскольку нет причин для раскрытия InputStream вне области действия этого метода, у вас есть проблема с тестированием.
Но я полагаю, что вы не заботитесь о закрытии InputStream
. Вы хотите проверить это, потому что вам сказали, что это хорошая практика (и это так). Но я думаю, что вы на самом деле заботитесь о том, что негативное влияние потока остается открытым. Каков эффект?
Попробуйте изменить этот метод, чтобы он не закрывал поток, а затем выполнял его много раз. У вас возникла утечка памяти или закончились файловые дескрипторы или какой-нибудь другой способ? Если это так, у вас есть разумный тест.
В качестве альтернативы, просто зайдите в раскрывающийся InputStream, который может сказать вам, был ли он закрыт или нет. Сделайте пакет защищенным. Это "нечистый", но прагматичный подход.
Ответ 3
Чтобы проверить, вызван ли метод close(), вы можете использовать Mockito.spy() для создания прокси-объекта, который может запоминать вызовы. Шпион делегирует все вызовы базовому InputStream, просто запоминает, что произошло:
InputStream inputStreamSpy = Mockito.spy(inputStream);
// a code that is expected to close your stream goes here ...
Mockito.verify(inputStreamSpy).close();
Это не решит ваши проблемы с инъецированием экземпляра InputStream. Похоже, вам нужен какой-то factory, который может открыть поток для вас, и вы можете издеваться над этим factory в модульных тестах. Позвольте называть эту factory файловую систему:
public class FileSystem {
public FileInputStream newFileInputStream(File file) {
return new FileInputStream(file);
}
}
Теперь вы можете ввести экземпляр FileSystem и не использовать ресурсы до запуска метода выполнения:
public void run() {
InputStream inputStream = null;
try {
inputStream = fileSystem.newFileInputStream(file);
//more stuff here
}
catch (Exception e) {
//simplified for reading
}
finally {
if(inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {}
}
}
}
@Test
public void runShouldCloseInputStream() {
InputStream inputStream = ...
InputStream inputStreamSpy = Mockito.spy(inputStream);
FileSystem fileSystemMock = Mockito.mock(FileSystem.class);
when(mockFileSystem.newFileInputStream(Mockito.any(File.class)))
.thenReturn(inputStreamSpy);
MyRunnable instance = new MyRunnable(mockFileSystem);
instance.run();
verify(inputStreamSpy).close();
}
Шпион может делать больше, чем просто слушать, вы можете научить его изменять поведение с помощью Mockito.when(), точно так же, как вы делали бы с обычным макетом.
Ответ 4
Вы можете сделать вот так:
try
{
inputStream.readLine();
}
catch (IOException e)
{
Assert.assertEquals(e.getLocalizedMessage(), "Stream closed");
}
Ответ 5
Вы можете написать в тесте что-то вроде:
try {
run();
} catch (IOException e) {
Assert.fail();
}
Когда ваш метод закроет strem и произойдет исключение, тогда тест завершится с ошибкой.