In software testing, particularly in the unit tests, verifying that the code throws the expected exceptions under certain conditions is crucial. JUnit is the widely used testing framework for the Java, it allows us to assert exceptions using the different approaches depending on the version (JUnit 4 or JUnit 5).
In this article, we will learn the methods to the assert exceptions in both JUnit 4 and JUnit 5, with step-by-step examples.
Prerequisites:
- Basic understanding of the Java and unit testing.
- JUnit 4 or JUnit 5 dependency in the project.
- Maven for building dependency management.
- JDK and IntelliJ IDEA installed in your system.
Exception Handling in JUnit 4
JUnit 4 provides two main approaches for handling the exceptions in the unit tests:
- Using the @Test(expected = Exception.class)
- Using the try-catch with assertions
Using @Test(expected = Exception.class)
In JUnit 4, the expected attribute of the @Test annotation allows you to specify an exception that you expect to be thrown. This approach is concise and efficient.
Example:
import org.junit.Test;
public class JUnit4ExceptionTest {
@Test(expected = IllegalArgumentException.class)
public void testExceptionThrown() {
// Code that should throw IllegalArgumentException
throw new IllegalArgumentException("Invalid argument");
}
}
In this example:
- The
@Testannotation uses theexpectedattribute to specify the expected exception type. - The
testExceptionThrownmethod passes ifIllegalArgumentExceptionis thrown; otherwise, it fails.
Using Try-Catch with Assertions
Alternatively, you can use a try-catch block to catch exceptions manually and assert the exception type and message, allowing for more detailed assertions.
Example:
import org.junit.Assert;
import org.junit.Test;
public class JUnit4TryCatchTest {
@Test
public void testExceptionThrown() {
try {
throw new IllegalArgumentException("Invalid argument");
} catch (IllegalArgumentException e) {
Assert.assertEquals("Invalid argument", e.getMessage());
}
}
}
In this example,
- The try-catch block catches
IllegalArgumentException. - The
Assert.assertEqualsmethod verifies that the exception message matches the expected value.
Exception Handling in JUnit 5
JUnit 5 offers a approach for asserting exceptions using Assertions.assertThrows(), which captures the thrown exception and allows further assertions.
Using Assertions.assertThrows()
The assertThrows method is preferred for testing exceptions in JUnit 5. It requires specifying the exception type and a lambda expression that includes the code expected to throw the exception.
Example:
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class JUnit5ExceptionTest {
@Test
public void testExceptionThrown() {
IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class, () -> {
throw new IllegalArgumentException("Invalid argument");
});
Assertions.assertEquals("Invalid argument", exception.getMessage());
}
}
In this example,
Assertions.assertThrowsis used to assert thatIllegalArgumentExceptionis thrown.- The returned exception allows further assertions, such as verifying the exception message.
Testing Absence of Exceptions Using assertDoesNotThrow()
JUnit 5 introduces assertDoesNotThrow(), which ensures that no exception is thrown from a given code segment.
Example:
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class JUnit5NoExceptionTest {
@Test
public void testNoExceptionThrown() {
Assertions.assertDoesNotThrow(() -> {
});
}
}
This method is helpful when you need to verify that certain code does not throw exceptions, providing a cleaner alternative than JUnit 4.
Project Implementation to Assert Exceptions in JUnit 4 and JUnit 5
In this example project, we demonstrate how to assert exceptions in both JUnit 4 and JUnit 5 using a simple service class that throws an exception under specific conditions.
Step 1: Create a Maven Project
- Project Name:
exception-handling-example - Build System: Maven
Click on the Create button.

Project Structure
After project creation done, set the project folder structure as shown in the below image:

Step 2: Add the JUnit 4 and JUnit 5 Dependencies to pom.xml
Open the pom.xml file and add the JUnit 4 and JUnit 5 dependencies into the Maven project.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="https://maven.apache.org/POM/4.0.0"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.gfg</groupId>
<artifactId>exception-handling-example</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- JUnit 4 Dependency -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!-- JUnit 5 Dependency -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<!-- JUnit Vintage Engine for running JUnit 4 tests in JUnit 5 -->
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<!-- Maven Surefire Plugin for JUnit 5 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<includes>
<include>**/*Test.java</include>
</includes>
</configuration>
</plugin>
</plugins>
</build>
</project>
Step 3: Service Class
MyService.java:
package com.gfg;
public class MyService {
public void validateInput(int number) {
if (number < 0) {
throw new InvalidInputException("Negative numbers are not allowed.");
}
}
}
Step 4: Custom Exception Class
InvalidInputException.java:
package com.gfg;
public class InvalidInputException extends RuntimeException {
public InvalidInputException(String message) {
super(message);
}
}
Step 5: Main Class
package com.gfg;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
MyService myService = new MyService();
Scanner scanner = new Scanner(System.in);
System.out.println("Enter a number:");
int input = scanner.nextInt();
try {
myService.validateInput(input);
} catch (InvalidInputException e) {
System.err.println("Error: " + e.getMessage());
} finally {
scanner.close();
}
}
}
Step 6: JUnit 4 Test Class
MyServiceJUnit4Test.java:
import com.gfg.InvalidInputException;
import com.gfg.MyService;
import org.junit.Assert;
import org.junit.Test;
public class MyServiceJUnit4Test {
private final MyService myService = new MyService();
@Test(expected = InvalidInputException.class)
public void testValidateInputThrowsException() {
myService.validateInput(-1);
}
@Test
public void testValidateInputExceptionMessage() {
try {
myService.validateInput(-1);
} catch (InvalidInputException e) {
Assert.assertEquals("Negative numbers are not allowed.", e.getMessage());
}
}
}
Step 7: JUnit 5 Test Class
MyServiceJUnit5Test.java:
import com.gfg.InvalidInputException;
import com.gfg.MyService;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class MyServiceJUnit5Test {
private final MyService myService = new MyService();
@Test
public void testValidateInputThrowsException() {
InvalidInputException exception = Assertions.assertThrows(InvalidInputException.class, () -> {
myService.validateInput(-1);
});
Assertions.assertEquals("Negative numbers are not allowed.", exception.getMessage());
}
}
Step 8: Run the Application
Once the project is completed, we will run the application and it will display the below output:

Exception case:

Step 9: Running the test
We will now run the tests using the below command.
mvn testOutput:

We should see the output indicating that all the tests passed successfully.
This example project demonstrates how to assert exceptions in both JUnit 4 and JUnit 5. By using both JUnit versions, we can see the differences in the syntax and flexibility when handling exceptions. It can be extended with more complex scenarios, but it serves as the foundational reference for exception handling in the unit tests.