Force IOException during file reading
I have a piece of code that reads data from a file. I want to force IOException in this code for testing purposes (I want to check if the code throws a correct custom exception in this case).
Is there any way to create a file which is protected from being read, for e开发者_如何学编程xample? Maybe dealing with some security checks can help?
Please, note that passing the name of a non-existent file cannot help, because FileNotFoundException has a separate catch clause.
Here is the piece of code for better understanding of the question:
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
}
}
}
Disclaimer: I have not tested this on a non-Windows platform, so it may have different results on a platform with different file locking characteristics.
If you lock the file beforehand, you can trigger an IOException when something attempts to read from it:
java.io.IOException: The process cannot access the file because another process has locked a portion of the file
This works even if you are in the same thread.
Here's some sample code:
final RandomAccessFile raFile = new RandomAccessFile(csvFile, "rw");
raFile.getChannel().lock();
If you can refactor the code slightly to accept a Reader, rather than a filename, you can use mocks. With EasyMock you can create a mock Reader, and set it to throw IOException upon calling any of its methods you wish. Then you just pass it to the method to be tested, and watch what happens :-)
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
}
}
}
}
then the test code:
mockReader = createMock(Reader.class);
expect(mockReader.readLine()).andThrow(
new IOException("Something terrible happened"));
replay(mockReader);
objectToTest.readFile(reader);
You could force an exception by invoking the close method on your 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
}
I hope that helps.
Define in your test an overloaded fileInputStream that throws an exception
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);
}
Late to the party, but I found a way to force an IOException. I am saving/reading a text file using BufferedWriter/Reader and am doing a test that looks like this:
public void testIOException() {
try {
int highScore = loadHighScore("/");
fail("IOException should have been thrown");
} catch (MissingFileException e) {
fail("IOException should have been thrown");
} catch (IOException e) {
// expected
}
}
Where "/" is the filename. Inside loadHighScore there's the line:
BufferedWriter bw = new BufferedWriter(new FileWriter(file));
Because my test inputs "file" as "/", it outputs an IOException
You could try creating the file as a superuser and then reading it as a standard user. There should be permissions issues there. Or just chmod the thing assuming you're on Linux. You can also try putting it in a hidden / protected directory.
You can mock java.nio.file.FileSystem
from JDK7. I wrote http://github.com/dernasherbrezon/mockfs specifically for generating IOExceptions during the test.
You can use a Mock library like Mockito or Easymock (+classpath) to create a Mock file object (newer libs have classloader extensions that let you mock concrete classes like File), or can cooperate with something like PowerMock (see blog) and have a mock generated for the constructor call, and throw the appropriate exception when called.
You can use for example Mockito to emulate that, however you would need to refactor your code so it were better testable. I would also advise to use try-with-resources if you use java 7+. Your code will look much cleaner.
You might extract the reader creation to a separate method so you could then replace an implementation with a mock. Like this
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);
}
}
}
The test would look as following
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);
}
}
This is how your someMethod might look like if you use 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 times shorter 20 times more readable
PS as another option for the test, you might extend your SomeClass in the test class and override someMethod in the test class instead of creating an unanimous implementation. But I like the first option more. It is a matter of taste though.
Hope that helps.
PPS: Just realized the question was asked years ago. :) Hopefully it helps somebody to find an answer these days.
You could make this exception raise for a file that's too large.
I would recommend against using the FileNotFoundException. If you want to throw a specific exception for the file not existing, I would check csvFile.exists().
For example, you can make csvFile a directory. I tested your code and it threw:
File file = new File("actually_a_directory.csv");
file.mkdir();
yourCode(file); // throws FileNotFoundException: "actually_a_directory.csv (Access is denied)"
In my opinion, a file actually being a directory is not the same as FileNotFound, yet it acts the same in java.
You can always throw your own IOException
:
throw new IOException("Test IOException");
精彩评论