I'll try to provide a hackneyed, useless example that reduces the problem nicely :-)
I have a GenericException
, and a MoreSpecificException
which extends GenericException
.
I need to test that SomeService.doThis()
throws a MoreSpecificException
. JUnit lets me do this elegantly like so.
@Test(expected = MoreSpecificException.class)
public void testDoThis() throws GenericException {
new SomeService().doThis();
}
However, I also need to test that SomeService.doThat()
throws a GenericException
, so I tried this.
@Test(expected = GenericException.class)
public void testDoThat() throws GenericException {
new SomeService().doThat();
}
However, I found that if doThat()
actually throws a MoreSpecificException
then the second test still passes. I assume this is because MoreSpecificException
is a GenericException
and the annotation is implemented to respect that relationship.
While this is a sensible default behaviour, I don't want this. I want to test that doThat()
throws a GenericException
and only a GenericException
. If it throws a MoreSpecificException
or any other subclass of GenericException
, I want the test to fail.
Reading the docs it doesn't seem I can do anything with the annotation to change this behaviour, so looks like I'll have to use another solution.
At the moment I'm resorting to the following ugly solution - EDIT made significantly less ugly by Nathan Hughes' answer :-)
@Test
public void testDoThat() {
try {
new SomeService().doThat();
Assert.fail();
} catch(GenericException ex) {
Assert.assertEquals(GenericException.class, ex.getClass());
}
}
Is there a more elegant way to achieve what I want within the JUnit framework?
Then in the test method you can use its expect () and expectMessage () to assert the type of expected exception and the exception message. In older versions of JUnit 4, you can specify the expected exception in the @Test annotation like this:
JUnit provides the facility to trace the exception and also to check whether the code is throwing expected exception or not. Junit4 provides an easy and readable way for exception testing, you can use
This is because you are expecting an exception from the method you are Unit Testing, otherwise our JUnit test would fail. Example@Test (expected=IllegalArgumentException.class)
By using "expected" parameter, you can specify the exception name our test may throw. In above example, you are using " IllegalArgumentException" which will be thrown by the test if a developer uses an argument which is not permitted. Let's understand exception testing by creating a Java class with a method throwing an exception.
The most elegant solution ;) Readable, without boilerplate code.
@Test
public void testDoThat() {
when(new SomeService()).doThat();
then(caughtException()).isExactlyInstanceOf(GenericException.class);
}
The code is identical for FEST Assertions 2 + Catch-Exceptions.
org.assertj:assertj-core:1.4.0
com.googlecode.catch-exception:catch-exception:1.2.0
You can assert that the class of the Exception is what you expect:
@Test
public void testDoThat() {
try {
new SomeService().doThat();
Assert.fail();
} catch(GenericException ex) {
assertEquals(GenericException.class, ex.getClass());
}
}
Also got rid of the flag, instead having the test fail if no exception is thrown.
You can use the ExpectedException rule and a custom Hamcrest matcher that specifies which class can be thrown.
The following test will print that you expected an instance of RuntimeException, but got an IllegalArgumentException.
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void testThrows() {
thrown.expect(isClass(RuntimeException.class));
throw new IllegalArgumentException("FAKE");
}
public class ClassMatchMatcher extends BaseMatcher<Object> {
private final Class<?> expectedClass;
private ClassMatchMatcher(Class<?> expectedClass) {
this.expectedClass = expectedClass;
}
@Override
public boolean matches(Object item) {
return expectedClass.equals(item.getClass());
}
@Override
public void describeTo(Description description) {
description.appendText("an instance of ")
.appendText(expectedClass.getName());
}
}
public class ExtraMatchers {
public static Matcher<Object> isClass(Class<?> aClass) {
return new ClassMatchMatcher(aClass);
}
}
Edit: Added a static factory method to make the test code cleaner.
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