junit

JUnit Exception Handling Example

1. Introduction

There are popular ways to test exception in JUnit. A developer can use the traditional try-catch statement, the @Rule or the annotation based. In this post, I’ll be discussing these 3 simple and easy to implement ways to make your test case and functional scenarios bulletproof of incoming exceptions.

2. Source(s)

In this example, we use the traditional way catching an exception. We use the try-catch clause to catch and throw an assertion condition that returns the test case result.
 

JUnitTryCatchExample.java

package com.areyes1.junit.exceptions;

import static org.junit.Assert.*;
import org.junit.Test;

public class JUnitTryCatchExample {

	double value = 0.0d;

	/**
	 * We are catching the exception using the traditional try-catch clause and return
	 * an assertion.
	 */
	@Test
	public void testJUnitTryCatch() {
		try {
			if(value < 0.1d) {
				fail("Value given is not as expected");
			}
		} catch (Exception e) {
			assertTrue((value != 0.0d));
		}

	}
}

Let’s go through the code.

  1. We introduce a double variable equating to 0.0. This will be our basis of the test case
  2. We created a method and tag it as @Test as this is a test case.
  3. We introduce a try catch and wrap the process of our test case. In our example, we want to check if the value is less the 0.1. If it does, it manually fail and throws the exception
  4. The exception then evaluate an assertion and throws the overall result of the test case.

This is a basic example of how can we use the traditional try-catch clause on our test case, now let’s look at on the annotation based approach.

JUnitRuleAnnotation.java

package com.areyes1.junit.exceptions;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

public class JUnitRuleAnnotation {

	//	We introduce this to create an expected exception object
	@Rule
	public ExpectedException expectedThrown = ExpectedException.none();
	
	/**
	 * The test is expecting throw the expected exception (which in this case, the NumberFormatException)
	 */
	@Test
	public void testExpectedRuleException() {
		expectedThrown.expect(NumberFormatException.class);
		String numberStr = "abc";
		Integer.valueOf(numberStr);
		
	}
	
}

Unlike the try-catch statement clause usage, we use the @Rule annotation to create an expected exception object. This is used to create an expected object exception for the test case to make sure that it throws the expected exception.

Let’s go deep into our code.

  1. We introduce a @Rule annotation. This is a specialized annotation to tag an object that we are imposing a rule for our test case.
  2. We introduce the test method using @Test
  3. We use the expectedThrown method and pass over the expected exception. This will then be used on the evaluation of the test case method.
  4. Finally we introduce the actual code that we wanted to test. In this case, we want to test the integer boxing of a string. We are expecting that it throws a NumberFormatException.

The @Rule annotation allows us to specify a specific Exception class that the test case expects. This is a very powerful Junit feature as it will allow developers to fail proof their methods.

Aside from the @Rule, we can actually pass an expected exception class on the @Test annotation. See example below.

JUnitAnnotationExample.java

package com.areyes1.junit.exceptions;

import org.junit.Test;

public class JUnitAnnotationExample {
	
	/**
	 * This means that the method is expecting a number format exception.
	 */
	@Test(expected=NumberFormatException.class)
	public void testAnnotationExample() {
		String numberStr = "abc";
		Integer.valueOf(numberStr);
	}
}

My personal favourite, using annotations only. The @Test annotation accepts an expected and message parameter, to tag the test method that it will only return the expected exception and the message it will give you if ever it didn’t.

Let’s go over the method

  1. We create a typical annotation based test case using @Test
  2. Instead of creating an @Rule, we use the expected attribute of the @Test to pass the expected exception.
  3. We test our code with it.

This is somehow a more cleaner and straightforward approach. Instead of creating an @Rule explicitly, we use the expected attribute for our test case.

3. Add-on: Custom Annotations

It is possible to create a custom annotation class to be used for test cases. This will allow developers to fully customise the behaviour of the test case once an assertion is evaluated and an exception is thrown.

StringCalculatorTest.java

@RunWith(ExpectsExceptionRunner.class)
public class StringCalculatorTest {
    @Test
    @ExpectsException(type = IllegalArgumentException.class, message = "negatives not allowed: [-1]")
    public void throwsExceptionWhenNegativeNumbersAreGiven() throws Exception {
        // act
        calculator.add("-1,-2,3");
    }
   
}

the annotation
ExpectsException.java

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface ExpectsException {
    Class type();
 
    String message() default "";
}

runner class (copy pasted code)
ExpectsExceptionRunner.java

public class ExpectsExceptionRunner extends BlockJUnit4ClassRunner {
    public ExpectsExceptionRunner(Class klass) throws InitializationError {
        super(klass);
    }
 
    @Override
    protected Statement possiblyExpectingExceptions(FrameworkMethod method, Object test, Statement next) {
        ExpectsException annotation = method.getAnnotation(ExpectsException.class);
        if (annotation == null) {
            return next;
        }
        return new ExpectExceptionWithMessage(next, annotation.type(), annotation.message());
    }
 
    class ExpectExceptionWithMessage extends Statement {
 
        private final Statement next;
        private final Class expected;
        private final String expectedMessage;
 
        public ExpectExceptionWithMessage(Statement next, Class expected, String expectedMessage) {
            this.next = next;
            this.expected = expected;
            this.expectedMessage = expectedMessage;
        }
 
        @Override
        public void evaluate() throws Exception {
            boolean complete = false;
            try {
                next.evaluate();
                complete = true;
            } catch (AssumptionViolatedException e) {
                throw e;
            } catch (Throwable e) {
                if (!expected.isAssignableFrom(e.getClass())) {
                    String message = "Unexpected exception, expected but was ";
                    throw new Exception(message, e);
                }
 
                if (isNotNull(expectedMessage) && !expectedMessage.equals(e.getMessage())) {
                    String message = "Unexpected exception message, expected but was";
                    throw new Exception(message, e);
                }
            }
            if (complete) {
                throw new AssertionError("Expected exception: "
                        + expected.getName());
            }
        }
 
        private boolean isNotNull(String s) {
            return s != null && !s.isEmpty();
        }
    }
 
}

4. Download the Eclipse project of this tutorial:

This was an example of testing exceptions with JUnit.

Download
You can download the full source code of this example here: junit-exception-example

Alvin Reyes

Alvin has an Information Technology Degree from Mapua Institute of Technology. During his studies, he was already heavily involved in a number of small to large projects where he primarily contributes by doing programming, analysis design. After graduating, he continued to do side projects on Mobile, Desktop and Web Applications.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button