Skip to content

Embed URL

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
A new twist on an old pattern for checking for exceptions
@Test
public void ioFailure() throws Exception {
final IOException ioFailure = new IOException("Simulating a failure writing to the file.");
try {
new WriteTextToFileActionImpl() {
@Override
protected FileWriter fileWriterOn(File path) throws IOException {
return new FileWriter(path) {
@Override
public void write(String str, int off, int len) throws IOException {
throw ioFailure;
}
};
}
}.writeTextToFile("::text::", new File("anyWritableFile.txt"));
fail("How did you survive the I/O failure?!");
} catch (IOException success) {
if (success != ioFailure)
throw success;
}
}
@jbrains
Owner

I used to write assertSame(ioFailure, success), but when the production code threw a different exception of the same type, I couldn't easily see the stack trace that it actually threw. Rethrowing the exception when it's not the one I expect solved that problem.

@npryce

Why do you care exactly which exception instance bubbles out? The information in the exception is important but how the code gets it out to the caller is not. Eg if you change the code to wrap the exception or whatever the test will break for an unimportant reason.

@jbrains
Owner

In this particular case, I want to distinguish IOExceptions for different reasons. This test had failed because it couldn't write to the File, which threw an IOException, but it was the wrong exception, and caused the test to pass for the wrong reason.

@SamirTalwar

I'd prefer to match on the message. I don't care if it's the right exception, just that the exception tells me what's wrong.

Using youDevise Matchers:

assertThat(running(new Action() {
    @Override public void execute() {
        writeTextToFileAction.writeTextToFile("::text::", new File("anyWritableFile.txt"));
    }
}), throwsException(anExceptionOfType(IOException.class).withTheMessage("Simulating a failure writing to the file.")));

Or in Java 8:

assertThat(running(() -> { writeTextToFileAction.writeTextToFile("::text::", new File("anyWritableFile.txt")); }),
           throwsException(anExceptionOfType(IOException.class).withTheMessage("Simulating a failure writing to the file.")));

Of course, if you really cared it was the same exception, you could do this:

assertThat(running(() -> { writeTextToFileAction.writeTextToFile("::text::", new File("anyWritableFile.txt")); }),
           throwsException(sameInstance(ioException)));
@jbrains
Owner

Thank you for the tip on youDevise/matchers. I don't typically like to match on exception messages, because that makes the assertion hyperactive (fail too easily), but I see how not depending on the exception type/instance would provide a different dimension of freedom to change the code. It feels like a style choice to me, but only because I haven't tried it your way extensively yet.

Either way, I do like the running(...).throwsException(...) style. Thanks for that.

@SamirTalwar

Glad I could help. I should probably admit that I wrote that particular matcher when I worked for youDevise, so I'm partial to it. I'm hoping that it'll make it into Hamcrest at some point.

If you want to match on messages but not worry about punctuation, etc. you can always provide a matcher to withTheMessage. For example: withTheMessage(containsString("failure")).

@jbrains
Owner

Thanks again. I just think in terms of regexes, so I naturally jump there first. (I know... now I have two problems.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.