• JUnit provides an option of tracing the exception handling of code. You can test whether the code throws a desired exception or not. The expected parameter is used along with @Test annotation.
  • An exception is an event that occurs during the execution of a program that disrupts the normal flow of instructions.
  • Java programming language provides exceptions to deal with errors and other exceptional events in the code.
  • The biggest advantage of exceptions is that they simply allow you to separate error-handling code from regular code.
  • This improves the robustness and readability of programs created in Java.
  • Java provides several techniques to effectively work with exceptions:
      try, catch, and finally − to handle exceptions,
      try-with-resources statement − to work with resources,
      throw/throws − to throw and declare exceptions respectively.
  • In JUnit, we may employ many techniques for testing exceptions including:
        try-catch idiom
        With JUnit rule
        With @Test annotation
        With catch-exception library
        With custom annotation
        With Lambda expression (as of Java 1.8)
        With AssertJ 3.0.0 for Java 8

try-catch idiom

  • This idiom is one of the most popular one, because it was used already in JUnit 3.
Java Code
public void throwsExceptionWhenNegativeNumbersAreGiven() {
try {
calculator.add("-1,-2,3");
fail("Should throw an exception if one or more of given numbers are negative");
} catch (Exception e) {
assertThat(e)
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("negatives not allowed: [-1, -2]");
}
}
[ad type=”banner”]

The above approach is a common pattern. The test will fail when no exception is thrown and the exception itself is verified in a catch clause (in the above example I used the FEST Fluent Assertions) and preferring the approach with ExpectedException rule executes better results

With JUnit rule

The same example can be created using ExceptedException rule. The rule must be a public field marked with @Rule annotation. Please note that the “thrown” rule may be reused in many tests.

Java Code
@Rule
public ExpectedException thrown = ExpectedException.none();

@Test
public void throwsExceptionWhenNegativeNumbersAreGiven() {
// arrange
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage(equalTo("negatives not allowed: [-1, -2]"));
// act
calculator.add("-1,-2,3");
}

When the exception isn’t thrown you will get the following message: java.lang.AssertionError: Expected test to throw (an instance of java.lang.IllegalArgumentException and exception with message “negatives not allowed: [-1, -2]”)

But not all the exception it checks only the type of the exception thrown and then we have to make use @Test annotation.

With annotation

Java Code
@Test (expected = IllegalArgumentException.class)
public void throwsExceptionWhenNegativeNumbersAreGiven() {
// act
calculator.add("-1,-2,3");
}

When the exception wasn’t thrown you will get the following message: java.lang.AssertionError: Expected exception: java.lang.IllegalArgumentException

Sometimes the approach tempts to expect general Exception, RuntimeException or even a Throwable. And this is considered as a bad practice, because the code may throw exception in other place than actually expected and the test will still pass

  • With JUnit rule and with annotation: The advantages are,
      Error messages when the code does not throw an exception are automagically handled
      The readability is improved
      There is less code to be created

AssertJ assertion

Java Code
import static org.assertj.core.api.Assertions.*;

@Test
public void testFooThrowsIndexOutOfBoundsException() {
Foo foo = new Foo();

assertThatThrownBy(() -> foo.doStuff())
.isInstanceOf(IndexOutOfBoundsException.class);
}
[ad type=”banner”]

It’s better than @Test(expected=IndexOutOfBoundsException.class) because it guarantees the expected line in the test threw the exception and lets you check more details about the exception, such as message, easier:

Java Code
assertThatThrownBy(() ->
{
throw new Exception("boom!");
})
.isInstanceOf(Exception.class)
.hasMessageContaining("boom");

ExpectedException Rule

  • Alternatively, use the ExpectedException rule. This rule lets you indicate not only what exception you are expecting, but also the exception message you are expecting:
Java Code
@Rule
public ExpectedException thrown = ExpectedException.none();

@Test
public void shouldTestExceptionMessage() throws IndexOutOfBoundsException {
List<Object> list = new ArrayList<Object>();

thrown.expect(IndexOutOfBoundsException.class);
thrown.expectMessage("Index: 0, Size: 0");
list.get(0); // execution will never get past this line
}

The expectMessage also lets you use Matchers, which gives you a bit more flexibility in your tests. An example:

Java Code
thrown.expectMessage(Matchers.containsString("Size: 0"));

Combining Rules with @RunWith

  • Beware though that if we combine the rule with certain @RunWith classes, we may get a false positive. Specifically, if you were to run with a class that extends JUnit4ClassRunner, the test would no longer fail. We’d get a false positive.
  • For example, if you’re using a version of JMock prior to 2.6.0 and use @RunWith(JMock.class) we’ll encounter this. Older versions of the JMock.class extend JUnit4ClassRunner and JUnit4ClassRunner ignores rules. The newer BlockJUnit4ClassRunner supports rules and JMock post 2.6.0 extends this class in JMock.class.
[ad type=”banner”]

Categorized in: