The old way
There are a few ways of testing for exceptions. The old way was to just catch the exception and then do any asserts that you need. See below:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Test | |
public void testCheckExceptionWithTryCatch() | |
{ | |
Address address = new Address("Mr. Bear"); | |
try | |
{ | |
address.setName(null); | |
fail("Exception expected."); | |
} catch (NullPointerException exception) | |
{ | |
assertThat(exception.getMessage(), equalTo("the name may not be null")); | |
} | |
assertThat(address.getName(), not(nullValue())); | |
} |
Advantages:
- you can do anything you like
- boilerplate takes up a large part
- errorprone, for example forgetting to add the fail, means the testcase will pass if there's no exception.
TestNG
TestNG provides an extention to the @Test annotation that checks for exceptions. In the example below, it is even possible to check that the appropriate exception message is returned (using a regular expression).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* | |
* @author mrbear | |
*/ | |
public class AddressNGTest | |
{ | |
public AddressNGTest() | |
{ | |
} | |
@Test(expectedExceptions = NullPointerException.class) | |
public void testWithNullpointerException() | |
{ | |
Address address = new Address(null); | |
} | |
@Test(expectedExceptions = NullPointerException.class, expectedExceptionsMessageRegExp = "the name may not be null") | |
public void testCheckExceptionMessage() | |
{ | |
Address address = new Address(null); | |
} | |
} |
Advantages:
- it is immediately clear that the test tests an error case
- not possible to do any checks or asserts after the exception is thrown
His argument is, if your test is both verifying data as well as exceptions, you could pull these two apart into two separate tests. One test that tests for the exception, and one test that verifies the data. I countered that, as in his case, both tests verify the exact same behaviour in the SUT (System Under Test), it should be one test. Let me know what your opinions are.
JUnit
JUnit, in the new version (since 4.7), has an additional solution based on mocking stuff:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* | |
* @author mrbear | |
*/ | |
public class AddressJUnitTest | |
{ | |
@Rule | |
public ExpectedException nameMayNotBeNull = ExpectedException.none(); | |
@Test | |
public void testWithNullpointerExceptionInJUnit() | |
{ | |
nameMayNotBeNull.expect(NullPointerException.class); | |
nameMayNotBeNull.expectMessage("the name may not be null"); | |
Address mrbear = new Address("Mr. Bear"); | |
mrbear.setName(null); | |
assertThat(mrbear.getName(), not(nullValue())); | |
} | |
} |
Advantages:
- asserts after the exception is thrown become possible
- you can do anything you like
- a little boilerplate
- not immediately clear that it's an exceptional testcase
- I don't much like mocking
References
- StackOverflow - JUnit Testing Exceptions
- http://stackoverflow.com/questions/15216438/junit-testing-exceptions
- StackOverflow - JUnit test analysing expected exceptions
- http://stackoverflow.com/questions/4489801/junit-test-analysing-expected-exceptions