I'm writing tests to validate my methods' contracts, e.g., a parameter cannot be null
, an int
param must be positive, etc. When a contract is violated, the thrown exception type reflects why it failed, such as NullPointerException
or IllegalArgumentException
. Sometimes I don't care which of these two is thrown, only that one of them is thrown. (Yes, I should be testing for the exact exception type, but bear with me...)
I could use JUnit's @Test(expected = RuntimeException.class)
(since RuntimeException
is the closest common parent of IAE
and NPE
), but this is overly generic as the method under test might throw some other RuntimeException
.
Instead, I'm trying to use JUnit's ExpectedExceptions
class.
Here is a complete example:
import org.hamcrest.CoreMatchers;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import static org.hamcrest.CoreMatchers.anyOf;
public class ParameterViolationTest {
private Target target = new Target();
@Rule
public ExpectedException expected = ExpectedException.none();
@Test
public void parameterViolation() {
expected.expect(anyOf(
CoreMatchers.<Class<? extends Exception>>
equalTo(NullPointerException.class),
CoreMatchers.<Class<? extends Exception>>
equalTo(IllegalArgumentException.class)));
// Null parameter violates contract, throws some exception.
target.methodUnderTest(null);
}
private static class Target {
public void methodUnderTest(Object o) {
if (o == null) {
// throw new IllegalArgumentException();
throw new NullPointerException();
}
}
}
}
I would expect this test to pass, but instead it fails:
java.lang.AssertionError:
Expected: (<class java.lang.NullPointerException> or <class java.lang.IllegalArgumentException>)
but: was <java.lang.NullPointerException>
Stacktrace was: java.lang.NullPointerException
at ParameterViolationTest$Target.methodUnderTest(ParameterViolationTest.java:35)
at ParameterViolationTest.parameterViolation(ParameterViolationTest.java:25)
< internal calls... >
at org.junit.rules.ExpectedException$ExpectedExceptionStatement.evaluate(ExpectedException.java:168)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
< internal calls... >
java.lang.AssertionError:
Expected: (<class java.lang.NullPointerException> or <class java.lang.IllegalArgumentException>)
but: was <java.lang.NullPointerException>
Stacktrace was: java.lang.NullPointerException
at ParameterViolationTest$Target.methodUnderTest(ParameterViolationTest.java:35)
at ParameterViolationTest.parameterViolation(ParameterViolationTest.java:25)
< internal calls... >
at org.junit.rules.ExpectedException$ExpectedExceptionStatement.evaluate(ExpectedException.java:168)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
< internal calls... >
at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
at org.junit.Assert.assertThat(Assert.java:865)
at org.junit.Assert.assertThat(Assert.java:832)
at org.junit.rules.ExpectedException.handleException(ExpectedException.java:198)
at org.junit.rules.ExpectedException.access$500(ExpectedException.java:85)
at org.junit.rules.ExpectedException$ExpectedExceptionStatement.evaluate(ExpectedException.java:177)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
< internal calls... >
What's going on here?
You have to use Matchers.instanceOf
instead of CoreMatchers.equalTo
, because you're comparing instances of XXXException
.
expected.expect(anyOf(
instanceOf(NullPointerException.class),
instanceOf(IllegalArgumentException.class)));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With