Welcome to this tutorial on testing exceptions in Java, focusing on using popular libraries like Mockito, AssertJ, and JUnit. This guide will walk you through how to effectively mock methods to throw exceptions during unit tests and then verify these exceptions are thrown as expected.
Introduction
When developing applications, handling exceptions is crucial for robust software development. Unit testing plays a vital role in ensuring your application behaves correctly under various scenarios, including exceptional cases. In this tutorial, we’ll explore techniques using Mockito—a popular mocking framework—and AssertJ—an expressive assertion library—to test exception-throwing behavior.
Prerequisites
Before you begin, ensure that you have the following installed:
- Java 8 or higher
- A build tool like Maven or Gradle (for dependency management)
- An IDE such as IntelliJ IDEA or Eclipse
We will use JUnit for writing our test cases. Ensure your project includes the necessary dependencies for JUnit, Mockito, and AssertJ.
Setting Up Dependencies
First, include the following dependencies in your pom.xml
if you’re using Maven:
<dependencies>
<!-- JUnit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!-- Mockito -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.12.4</version>
<scope>test</scope>
</dependency>
<!-- AssertJ -->
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.21.0</version>
<scope>test</scope>
</dependency>
</dependencies>
For Gradle, add the following to your build.gradle
:
dependencies {
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-core:3.12.4'
testImplementation 'org.assertj:assertj-core:3.21.0'
}
Mocking Exceptions with Mockito
Mockito allows you to define behavior for your mock objects, including throwing exceptions. Here’s how you can set up a method to throw an exception:
import static org.mockito.Mockito.*;
public class ServiceTest {
@Test
public void testExceptionThrow() {
// Arrange: Create a mock object and configure it to throw an exception.
SomeService service = mock(SomeService.class);
when(service.performAction()).thenThrow(new RuntimeException("Something went wrong"));
// Act & Assert: Verify the method throws the expected exception.
try {
service.performAction();
fail("Expected RuntimeException was not thrown");
} catch (RuntimeException e) {
assertEquals("Something went wrong", e.getMessage());
}
}
}
In this example, performAction()
is configured to throw a RuntimeException
. The test verifies that the exception is indeed thrown and checks its message.
Using AssertJ for Exception Assertions
AssertJ provides a fluent API for asserting exceptions. It’s particularly useful when combined with Java 8 lambdas:
import static org.assertj.core.api.Assertions.*;
public class ServiceTest {
@Test
public void shouldThrowException() {
// Arrange: Mock the service to throw an exception.
SomeService service = mock(SomeService.class);
given(service.performAction()).willThrow(new IllegalArgumentException("Invalid input"));
// Act & Assert: Use AssertJ to verify the exception.
assertThatThrownBy(() -> service.performAction())
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("Invalid input");
}
}
Here, assertThatThrownBy
is used to execute a lambda expression that triggers an exception. The assertion checks both the type of the exception and its message.
Using JUnit’s ExpectedException Rule
JUnit provides the ExpectedException
rule for asserting exceptions in tests:
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
public class ServiceTest {
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Test
public void testExceptionMessage() {
// Arrange: Mock the service to throw an exception.
SomeService service = mock(SomeService.class);
given(service.performAction()).willThrow(new IllegalArgumentException("Invalid input"));
// Act & Assert: Use JUnit's ExpectedException rule.
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Invalid input");
service.performAction();
}
}
This approach allows you to specify both the type and message of the expected exception using JUnit rules.
Conclusion
Testing exceptions is an essential part of ensuring your application handles error scenarios gracefully. By combining Mockito for mocking, AssertJ for fluent assertions, and JUnit’s capabilities, you can create comprehensive tests that verify exceptional behavior effectively. These tools not only make your tests more readable but also enhance their reliability by covering edge cases in your code.
Tips
- Always test both the occurrence and message of exceptions to ensure full coverage.
- Use AssertJ for its expressive assertions when working with Java 8 or later.
- Leverage JUnit’s
ExpectedException
rule if you prefer not using external libraries like Catch-Exception.
By following these practices, you can maintain robust and well-tested exception handling in your applications. Happy testing!