Junit’s annotation based expected exception testing

Junit provides an option of tracing exception thrown by a Java method. There are different ways of handling exceptions in JUnit tests. Standard Junit’s org.junit.Test annotation offers expected attribute that allows you specifying the a Throwable to cause a test method to succeed if an exception of the specified class is thrown by the method.

A Java unit test should verify correct exception thrown in exceptional case and no exception should be thrown in normal case. For this to test exception thrown by a Java method using @Test(expected=""), you need to use at least Junit 4.7.

One of the many situations where you need to test exception thrown by a Java method is testing an API method that should throw IllegalArgumentException if arguments passed to the method are not matching to pre-conditions.

For example, in order to test that we need to use @Test(expected=IllegalArgumentException.class) annotation. You can replace IllegalArgumentException.class with any other exception e.g. NullPointerException.class or ArithmeticException.class as per the requirement.

In order to test any Java method for throwing exception, you need to ensure that arguments provided to the method, from the test must result in expected Exception, otherwise Junit test will fail.

You may also like to read Junit 5 Expected Exception using assertThrows() Method.

Prerequisites

Java at least 8, Junit at least 4.7, Gradle 6.5.1 or Maven 3.6.3

Project Setup

You can create either gradle or maven based project in your favorite IDE or tool. The name of the project is java-junit-expected-exception.

For gradle based project you can use the following build.gradle script.

plugins {
    id 'java-library'
}

repositories {
    jcenter()
}

sourceCompatibility = 12
targetCompatibility = 12

dependencies {
    testImplementation 'junit:junit:4.13.1'
}

For maven based project you can use the following pom.xml file.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	
	<groupId>com.roytuts</groupId>
	<artifactId>java-junit-expected-exception</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>
	
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<java.version>at least 1.8</java.version>
	</properties>
	
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.13.1</version>
		</dependency>
	</dependencies>
	
	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.8.1</version>
                <configuration>
					<source>${java.version}</source>
					<target>${java.version}</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

Millisecond Converter Class

Here is an example of how to test exception thrown by a method called secsToMins(), which returns minutes as seconds/60, but before calculating minutes for given seconds it checks whether minutes are positive or not, and if minutes are zero or negative it throws IllegalArgumentException.

package com.roytuts.java.junit.expected.exception;

public class SecondsToMinutesUtils {

    public int secsToMins(int seconds) {
        if (seconds <= 0) {
            throw new IllegalArgumentException("seconds (" + seconds + ") cannot be 0 or negative");
        }
        
        return seconds / 60;
    }

}

Expected Exception

Below is the example for Exception testing, you can see that your testSecsToMins() method is annotated with @Test(expected=IllegalArgumentException.class), which means it expects an illegalArgumentException, when you run this JUnit test.

package com.roytuts.java.junit.expected.exception;

import static org.junit.Assert.assertEquals;

import org.junit.Before;
import org.junit.Test;

public class SecondsToMinutesUtilsExceptionTest {

    private SecondsToMinutesUtils secsToMins;

    @Before
    public void setUp() throws Exception {
        secsToMins = new SecondsToMinutesUtils();
    }

    @Test(expected = IllegalArgumentException.class)
    public void testSecsToMins() {
        int seconds = 0;
        int expResult = 0;
        
        int result = secsToMins.secsToMins(seconds); // should throw exception

        assertEquals(expResult, result);
    }

}

Now if you run the test class test will be passed with current arguments, but if you change arguments, for example, make int seconds = 2, the test will be failed like below, because the method would not throw IllegalArgumentException any more.

java.lang.AssertionError: Expected exception: java.lang.IllegalArgumentException
	at org.junit.internal.runners.statements.ExpectException.evaluate(ExpectException.java:34)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
	at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:542)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:770)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:464)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)

That’s all on how to test expected exception in JUnit4. It is an very easy to test Java method with Junit’s annotation based approach. Now you can easily verify any Java method for both correct and incorrect set of inputs, along with exceptions for normal and exceptional cases.

Source Code

Download

Leave a Comment