Introduction
In modern software development, ensuring that a program handles exceptions correctly is crucial for building robust applications. Exception testing in JUnit 5 allows developers to verify that their code properly handles error scenarios, improving reliability and maintainability. This blog post explores key techniques such as Testing for Exceptions in JUnit 5, Boundary Value Testing, and using AssertThrows to create effective test cases.
Testing for Exceptions in JUnit 5
JUnit 5 provides a streamlined way to test exceptions in Java applications. Unlike JUnit 4, which required using the expected
attribute or @Rule
, JUnit 5 introduces Assertions.assertThrows()
, offering a more flexible and readable approach.
Example of Exception Testing in JUnit 5
Consider a method that calculates the square root of a number. If a negative number is provided, it should throw an IllegalArgumentException
.
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
class MathUtilsTest {
double calculateSquareRoot(double number) {
if (number < 0) {
throw new IllegalArgumentException("Number must be non-negative");
}
return Math.sqrt(number);
}
@Test
void testCalculateSquareRootException() {
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
calculateSquareRoot(-5);
});
assertEquals("Number must be non-negative", exception.getMessage());
}
}
This test verifies that the method correctly throws an exception when given invalid input, ensuring robustness.
Boundary Value Testing
Boundary Value Testing (BVT) is a technique used to test the limits of input values. It focuses on edge cases, such as minimum and maximum values, where software is most likely to fail.
Example: Boundary Testing for Age Validation
Consider a function that validates a user’s age for registration, allowing only ages between 18 and 65.
boolean isValidAge(int age) {
return age >= 18 && age <= 65;
}
Boundary tests should check values just inside and just outside the valid range:
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
class AgeValidatorTest {
@Test
void testAgeBoundaries() {
assertTrue(isValidAge(18));
assertTrue(isValidAge(65));
assertFalse(isValidAge(17));
assertFalse(isValidAge(66));
}
}
BVT ensures the system correctly distinguishes between valid and invalid inputs.
Testing for Exceptions with AssertThrows
The assertThrows
method in JUnit 5 simplifies exception testing, making tests more readable and maintainable. It helps validate that methods correctly handle invalid inputs by throwing the expected exceptions.
Example: Division by Zero Handling
Consider a simple method that performs division:
int divide(int dividend, int divisor) {
if (divisor == 0) {
throw new ArithmeticException("Cannot divide by zero");
}
return dividend / divisor;
}
We can use assertThrows
to verify proper exception handling:
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
class DivisionTest {
@Test
void testDivideByZero() {
ArithmeticException exception = assertThrows(ArithmeticException.class, () -> {
divide(10, 0);
});
assertEquals("Cannot divide by zero", exception.getMessage());
}
}
This ensures that the division method correctly throws an exception when dividing by zero.
Conclusion
Testing exceptions effectively is a vital part of software quality assurance. JUnit 5’s assertThrows
method, combined with Boundary Value Testing, enables developers to create thorough test cases that improve the reliability and robustness of applications. By writing well-structured exception tests, developers can prevent unexpected failures and ensure their applications behave as expected under various conditions.
For further reading, check out:
#JUnit5 #ExceptionTesting #AssertThrows #BoundaryValueTesting
From the blog Rick’s Software Journal by RickDjouwe1 and used with permission of the author. All other rights reserved by the author.