Принуждение IOException во время чтения файла
У меня есть фрагмент кода, который считывает данные из файла. Я хочу заставить IOException в этом коде для тестирования (я хочу проверить, генерирует ли в этом случае правильный пользовательский исключение).
Есть ли способ создать файл, который защищен от чтения, например? Может быть, справиться с некоторыми проверками безопасности?
Обратите внимание, что передача имени несуществующего файла не может помочь, поскольку FileNotFoundException имеет отдельное предложение catch.
Вот фрагмент кода для лучшего понимания вопроса:
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(csvFile));
String rawLine;
while ((rawLine = reader.readLine()) != null) {
// some work is done here
}
} catch (FileNotFoundException e) {
throw new SomeCustomException();
} catch (IOException e) {
throw new SomeCustomException();
} finally {
// close the input stream
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
// ignore
}
}
}
Ответы
Ответ 1
Отказ от ответственности: я не тестировал это на платформе, отличной от Windows, поэтому она может иметь разные результаты на платформе с различными характеристиками блокировки файлов.
Если вы заблокируете файл заранее, вы можете вызвать IOException, когда что-то пытается прочитать из него:
java.io.IOException: The process cannot access the file because another process has locked a portion of the file
Это работает, даже если вы находитесь в одном потоке.
Вот пример кода:
final RandomAccessFile raFile = new RandomAccessFile(csvFile, "rw");
raFile.getChannel().lock();
Ответ 2
Если вы можете реорганизовать код немного, чтобы принять Reader, а не имя файла, вы можете использовать mocks. С помощью EasyMock вы можете создать mock Reader и настроить его на выброс IOException при вызове любого из его методов, которые вы хотите. Затем вы просто передаете его методу, который будет проверен, и посмотрите, что произойдет: -)
void readFile(Reader reader) throws SomeCustomException {
try {
String rawLine;
while ((rawLine = reader.readLine()) != null) {
// some work is done here
}
} catch (FileNotFoundException e) {
throw new SomeCustomException();
} catch (IOException e) {
throw new SomeCustomException();
} finally {
// close the input stream
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
// ignore
}
}
}
}
тогда тестовый код:
mockReader = createMock(Reader.class);
expect(mockReader.readLine()).andThrow(
new IOException("Something terrible happened"));
replay(mockReader);
objectToTest.readFile(reader);
Ответ 3
Вы можете попробовать создать файл в качестве суперпользователя, а затем прочитать его как стандартного пользователя. Там должны быть проблемы с разрешениями. Или просто chmod, предполагая, что вы работаете в Linux. Вы также можете попробовать поместить его в скрытый/защищенный каталог.
Ответ 4
Вы можете использовать библиотеку Mock, например Mockito или Перейти к началу страницы noreferrer" > PowerMock (см. блог) и создайте для вызова конструктора макет и выпустите соответствующее исключение при вызове.
Ответ 5
Вы можете вызвать исключение, вызвав метод close на BufferedReader
:
reader = new BufferedReader(new FileReader(csvFile));
// invoke the Close() method.
reader.Close();
String rawLine;
while ((rawLine = reader.readLine()) != null) {
// some work is done here
}
Я надеюсь, что это поможет.
Ответ 6
Вы можете сделать это повышение для слишком большого файла.
Ответ 7
Я бы рекомендовал не использовать FileNotFoundException. Если вы хотите бросить конкретное исключение для файла, который не существует, я бы проверял csvFile.exists().
Например, вы можете создать каталог csvFile. Я проверил ваш код, и он бросил:
File file = new File("actually_a_directory.csv");
file.mkdir();
yourCode(file); // throws FileNotFoundException: "actually_a_directory.csv (Access is denied)"
На мой взгляд, файл, фактически являющийся каталогом, не совпадает с FileNotFound, но он действует одинаково в java.
Ответ 8
Определите в своем тесте перегруженный файлInputStream, который генерирует исключение
FileInputStream s;
try {
s = new FileInputStream(fileName) {
@Override
public int read() throws IOException {
throw new IOException("Expected as a test");
}
};
} catch (FileNotFoundException e) {
throw new RuntimeException(e.getMessage(), e);
}
Ответ 9
Вы можете использовать, например, Mockito, чтобы подражать этому, однако вам нужно будет реорганизовать ваш код, чтобы он был лучше проверен. Я бы посоветовал использовать try-with-resources, если вы используете java 7+. Ваш код будет выглядеть намного чище.
Вы можете извлечь создание читателя в отдельный метод, чтобы затем заменить реализацию макетом. Как это
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class SomeClass {
public static final String IO_EXCEPTION = "IO Exception";
private String csvFile ="some_path";
public void someMethod() {
BufferedReader reader = null;
try {
reader = getReader();
String rawLine;
while ((rawLine = reader.readLine()) != null) {
// some work is done here
}
} catch (FileNotFoundException e) {
throw new SomeCustomException("FNF Exception");
} catch (IOException e) {
throw new SomeCustomException(IO_EXCEPTION);
} finally {
// close the input stream
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
// ignore
}
}
}
}
BufferedReader getReader() throws FileNotFoundException {
return new BufferedReader(new FileReader(csvFile));
}
class SomeCustomException extends RuntimeException {
public SomeCustomException(String message) {
super(message);
}
}
}
Тест будет выглядеть следующим образом:
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import SomeCustomException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
@RunWith(MockitoJUnitRunner.class)
public class SomeClassTest {
@Mock
private BufferedReader bufferedReader;
@Test
public void testMethod() throws IOException {
Mockito.when(bufferedReader.readLine()).thenThrow(new IOException());
SomeClass someClass = new SomeClass() {
@Override
BufferedReader getReader() throws FileNotFoundException {
return bufferedReader;
}
};
assertThatThrownBy(() -> someClass.someMethod()).isInstanceOf(SomeCustomException.class)
.hasMessage(SomeClass.IO_EXCEPTION);
}
}
Вот как выглядит ваш метод someMethod, если вы используете try-with-resource
public void someMethod() {
try(BufferedReader reader = getReader()) {
String rawLine;
while ((rawLine = reader.readLine()) != null) {
// some work is done here
}
} catch (FileNotFoundException e) {
throw new SomeCustomException("FNF Exception");
} catch (IOException e) {
throw new SomeCustomException(IO_EXCEPTION);
}
}
2 раза короче в 20 раз более читаемых
PS как еще один вариант теста, вы можете расширить свой SomeClass в тестовом классе и переопределить someMethod в тестовом классе вместо создания единодушной реализации. Но мне больше нравится первый вариант. Однако это вопрос вкуса.
Надеюсь, это поможет.
PPS: Просто понял, вопрос задавался много лет назад. :) Надеюсь, это помогает кому-то найти ответ в эти дни.
Ответ 10
Вы всегда можете создать свой собственный IOException
:
throw new IOException("Test IOException");