I wrote some test cases to test some method. But some methods throw an exception. Am I doing it correctly?
private void testNumber(String word, int number) {
An unexpected exception is a test failure, so you neither need nor want to catch one.
@Test
public void canConvertStringsToDecimals() {
String str = "1.234";
Assert.assertEquals(1.234, service.convert(str), 1.0e-4);
}
Until service
does not throw an IllegalArgumentException
because str
has a decimal point in it, that will be a simple test failure.
An expected exception should be handled by the optional expected
argument of @Test
.
@Test(expected=NullPointerException.class)
public void cannotConvertNulls() {
service.convert(null);
}
If the programmer was lazy and threw Exception
, or if he had service
return 0.0
, the test will fail. Only an NPE
will succeed. Note that subclasses of the expected exception also work. That's rare for NPE
s, but common with IOException
s and SQLException
s.
In the rare case that you want to test for a specific exception message, you use the newish ExpectedException
JUnit @Rule
.
@Rule
public ExpectedException thrown= ExpectedException.none();
@Test
public void messageIncludesErrantTemperature() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("-400"); // Tests that the message contains -400.
temperatureGauge.setTemperature(-400);
}
Now, unless the setTemperature throws an IAE
and the message contains the temperature the user was trying to set, the test fails. This rule can be used in more sophisticated ways.
Your example can best be handled by:
private void testNumber(String word, int number)
throws OutOfRangeNumberException {
assertEquals(word, service.convert(number));
}
@Test
public final void testZero()
throws OutOfRangeNumberException {
testNumber("zero", 0);
}
You can inline testNumber
; now, it does not help much. You can turn this into a parametrized test class.
You don't need to catch the exception to fail the test. Just let it go (by declaring throws
) and it will fail anyway.
Another case is when you actually expect the exception, then you put fail at the end of try block.
For example:
@Test
public void testInvalidNumber() {
try {
String dummy = service.convert(-1));
Assert.fail("Fail! Method was expected to throw an exception because negative numbers are not supported.")
} catch (OutOfRangeException e) {
// expected
}
}
You can use this kind of test to verify if your code is properly validating input and handles invalid input with a proper exception.
Remove the try-catch block and add throws Exception
to your test method, like:
@Test
public final void testZero() throws Exception {
assertEquals("zero", service.convert(0));
}
JUnit expects failing tests will throw Exceptions, your catching them is just stopping JUnit from being able to report them properly. Also this way the expected property on the @Test annotation will work.
There are several strategies that are open to you to deal with expected exceptions in your tests. I think the JUnit annotations and try/catch idiom have already been mentioned above. I'd like to draw attention to the Java 8 option of Lambda expressions.
For instance given:
class DummyService {
public void someMethod() {
throw new RuntimeException("Runtime exception occurred");
}
public void someOtherMethod(boolean b) {
throw new RuntimeException("Runtime exception occurred",
new IllegalStateException("Illegal state"));
}
}
You can do this:
@Test
public void verifiesCauseType() {
// lambda expression
assertThrown(() -> new DummyService().someOtherMethod(true))
// assertions
.isInstanceOf(RuntimeException.class)
.hasMessage("Runtime exception occurred")
.hasCauseInstanceOf(IllegalStateException.class);
}
Take a look at this blog which covers most of the options with examples.
http://blog.codeleak.pl/2013/07/3-ways-of-handling-exceptions-in-junit.html
And this one explains the Java 8 Lambda option more fully:
http://blog.codeleak.pl/2014/07/junit-testing-exception-with-java-8-and-lambda-expressions.html