Mockito throw exception

Updated on

0
(0)

To simulate exceptions with Mockito, here are the detailed steps:

👉 Skip the hassle and get the ready to use 100% working script (Link in the comments section of the YouTube Video) (Latest test 31/05/2025)

Check more on: How to Bypass Cloudflare Turnstile & Cloudflare WAF – Reddit, How to Bypass Cloudflare Turnstile, Cloudflare WAF & reCAPTCHA v3 – Medium, How to Bypass Cloudflare Turnstile, WAF & reCAPTCHA v3 – LinkedIn Article

  1. Understand the Goal: You want a mocked object’s method to throw a specific exception when called, instead of returning a value or doing nothing. This is crucial for testing error handling paths in your code.

  2. Basic doThrow.when Syntax: This is the most common and robust way.

    • Step 1: Create a mock.

      
      
      MyService mockService = mockMyService.class.
      
    • Step 2: Define the exception to throw.

      DoThrownew IllegalArgumentException”Invalid input”.whenmockService.someMethodanyString.
      This tells Mockito: “When mockService.someMethod is called with any string argument, throw an IllegalArgumentException.”

  3. Alternative when.thenThrow Syntax: This works for methods that return a value.

    UserRepository mockRepo = mockUserRepository.class.
    
    • Step 2: Define the exception.

      WhenmockRepo.findById1L.thenThrownew EntityNotFoundException”User not found”.

      This means: “When mockRepo.findById1L is called, throw an EntityNotFoundException.” Be aware: when.thenThrow cannot be used for void methods.

  4. Throwing Multiple Exceptions Sequentially:

    doThrownew IOException"First error"
        .doThrownew SQLException"Second error"
        .whenmockService.processData.
    

    The first call to processData will throw IOException, the second will throw SQLException. Subsequent calls will continue to throw SQLException.

  5. With Argument Matchers: For more specific scenarios.

    DoThrownew CustomValidationException”Specific validation failed”

    .whenmockValidator.validateargThatmyObject -> myObject.isValid == false.
    

    This throws an exception only when validate is called with an object that is not valid according to your custom matcher.

  6. URL for Official Documentation: For the most up-to-date and comprehensive details, always refer to the official Mockito documentation: https://site.mockito.org/. Specifically, look for sections on doThrow and thenThrow.

Table of Contents

Understanding Exception Handling in Unit Testing

Unit testing, a cornerstone of quality assurance, demands that we not only test the “happy path” where everything works as expected but also the “unhappy paths” where errors or exceptions occur.

Mockito, a popular mocking framework for Java, provides elegant ways to simulate these error conditions. This isn’t just about making your tests pass.

It’s about ensuring your application gracefully recovers or reports issues when external dependencies or internal logic fail.

Without thoroughly testing exception handling, your production system could crumble under unexpected circumstances, leading to poor user experience, data corruption, or security vulnerabilities.

The Criticality of Exception Testing

Why go to the trouble of faking exceptions? Imagine your application interacts with a database, an external API, or a file system.

What happens if the database connection drops, the API returns a 500 error, or a file isn’t found? Your application needs to respond predictably—perhaps by retrying the operation, logging the error, displaying an informative message to the user, or even escalating the issue.

If you don’t test these scenarios, you’re essentially launching your code into the wild without a safety net.

Industry data shows that a significant portion of production bugs are related to unhandled or improperly handled exceptions.

For instance, a 2021 report by Sentry indicated that JavaScript errors which include unhandled exceptions accounted for roughly 16% of all production issues, highlighting the widespread nature of this problem across programming paradigms.

Mockito’s Role in Simulating Failures

Mockito shines here by allowing you to isolate the unit under test e.g., a service method from its real dependencies. Build jobs in jenkins

Instead of needing a live database connection to test a SQLException, you can tell a mocked DataSource to throw one. This isolation makes tests:

  • Fast: No slow network or disk I/O.
  • Reliable: No external factors influencing test results.
  • Reproducible: Tests always run the same way, regardless of environment.
  • Comprehensive: You can simulate edge cases that are difficult or impossible to trigger in a real environment.

The doThrow.when Pattern: For Void Methods and More

The doThrow.when syntax is Mockito’s workhorse for simulating exceptions, particularly when dealing with methods that return void. However, its versatility extends beyond void methods, making it a highly recommended approach for consistency.

This pattern reads very naturally: “Do this throw an exception when this mock method is called.”

Why doThrow.when is Often Preferred

While when.thenThrow is available for methods that return a value, doThrow.when offers a more robust and flexible solution.

  • Handles void methods: This is its primary advantage. You cannot use when on a void method because when expects a return value from the method call to configure.
  • Clearer separation of action and mock: doThrow explicitly states what action Mockito should perform before the mocked method call is configured, making the intent very clear.
  • Supports chaining multiple behaviors: You can chain multiple doThrow, doReturn, doNothing, etc., calls to simulate different behaviors on sequential invocations of the same method.

Practical Application: Simulating a Database Error

Let’s say you have a UserService that depends on a UserRepository. You want to test what happens when the saveUser method in the UserRepository throws a SQLException because, for instance, the database connection is lost or a constraint is violated.

// The service class under test
public class UserService {
    private final UserRepository userRepository.



   public UserServiceUserRepository userRepository {
        this.userRepository = userRepository.
    }

    public User createUserUser user {
        try {
            return userRepository.saveUseruser.
        } catch SQLException e {


           // Log the error, throw a custom application exception, or return null


           System.err.println"Database error saving user: " + e.getMessage.


           throw new ApplicationException"Failed to create user due to database issue", e.
        }
}

// The repository interface
public interface UserRepository {
    User saveUserUser user throws SQLException. // Throws a checked exception

// In your test class
import org.junit.jupiter.api.Test.
import static org.mockito.Mockito.*.
import static org.junit.jupiter.api.Assertions.*.

public class UserServiceTest {

    @Test


   void createUser_shouldThrowApplicationException_whenUserRepositoryThrowsSQLException {
        // 1. Create a mock for the UserRepository


       UserRepository mockUserRepository = mockUserRepository.class.



       // 2. Configure the mock to throw SQLException when saveUser is called


       // Since saveUser returns a value, we can use either doThrow.when or when.thenThrow.


       // For consistency and general applicability, doThrow.when is often preferred.


           doThrownew SQLException"Duplicate entry detected".whenmockUserRepository.saveUseranyUser.class.


           // This catch block is just to satisfy the compiler for checked exceptions


           // In a real test, this won't be executed as Mockito handles it.



       // 3. Create the UserService instance with the mocked repository


       UserService userService = new UserServicemockUserRepository.



       // 4. Assert that calling createUser throws the expected ApplicationException


       User newUser = new User"testuser", "password123".



       // We expect ApplicationException to be thrown


       ApplicationException thrown = assertThrowsApplicationException.class,  -> {
            userService.createUsernewUser.
        }.



       // Optionally, assert the message or cause of the thrown exception


       assertTruethrown.getMessage.contains"Failed to create user".


       assertTruethrown.getCause instanceof SQLException.


       assertEquals"Duplicate entry detected", thrown.getCause.getMessage.



       // 5. Verify that saveUser was indeed called


           verifymockUserRepository.saveUsernewUser.


           // This catch block will never be hit during verification as it's not executing the method.

    // Dummy User class for example
    static class User {
        String username.
        String password.



       public UserString username, String password {
            this.username = username.
            this.password = password.



       // Getters and Setters omitted for brevity

    // Dummy ApplicationException for example


   static class ApplicationException extends RuntimeException {


       public ApplicationExceptionString message, Throwable cause {
            supermessage, cause.

In this example, we successfully simulated a SQLException from the UserRepository and verified that our UserService correctly caught it and re-threw an ApplicationException, demonstrating robust error handling.

The when.thenThrow Pattern: For Value-Returning Methods

While doThrow.when is versatile, Mockito also provides when.thenThrow specifically designed for methods that are expected to return a value. This syntax often feels more natural and fluent when configuring methods that have non-void return types. It directly answers the question: “When this method is called, then what should happen?”

When to Use when.thenThrow

You should opt for when.thenThrow when the method you are mocking has a non-void return type.

For example, if your method returns a String, an int, or an object like User or Optional<User>.

Illustrative Scenario: User Not Found

Consider a UserFinderService that retrieves a user by ID from a UserRepository. If the user is not found, you might want your UserRepository mock to throw a UserNotFoundException. WordPress accessibility plugins

public class UserFinderService {

public UserFinderServiceUserRepository userRepository {

 public User findUserByIdLong userId {
     // Attempt to find the user


    // This method might catch exceptions thrown by the repository


        return userRepository.findByIduserId.
     } catch UserNotFoundException e {


        // Log the specific error, perhaps throw a more generic


        // or specific business-level exception, or return null/Optional.empty


        System.err.println"User not found for ID: " + userId + ". Error: " + e.getMessage.


        // Re-throw a custom application-level exception for broader handling


        throw new ResourceNotFoundException"User with ID " + userId + " not found.", e.



User findByIdLong id throws UserNotFoundException. // Throws a custom checked exception

// Custom exception for when a user is not found
class UserNotFoundException extends Exception {
public UserNotFoundExceptionString message {
supermessage.

// Custom runtime exception for resource not found

Class ResourceNotFoundException extends RuntimeException {

public ResourceNotFoundExceptionString message, Throwable cause {
     supermessage, cause.

public class UserFinderServiceTest {

void findUserById_shouldThrowResourceNotFoundException_whenUserRepositoryThrowsUserNotFoundException {





    // 2. Configure the mock to throw UserNotFoundException when findById99L is called


    // Since findById returns a User, we use when.thenThrow.


        whenmockUserRepository.findById99L.thenThrownew UserNotFoundException"User with ID 99 does not exist".


        // This catch block is required by the compiler because UserNotFoundException is checked.


        // In practice, this block won't be hit during the test setup.



    // 3. Create the UserFinderService instance with the mocked repository


    UserFinderService userFinderService = new UserFinderServicemockUserRepository.



    // 4. Define the ID that will trigger the exception
     Long nonExistentUserId = 99L.



    // 5. Assert that calling findUserById throws the expected ResourceNotFoundException


    ResourceNotFoundException thrown = assertThrowsResourceNotFoundException.class,  -> {


        userFinderService.findUserByIdnonExistentUserId.





    assertTruethrown.getMessage.contains"User with ID 99 not found.".


    assertTruethrown.getCause instanceof UserNotFoundException.


    assertEquals"User with ID 99 does not exist", thrown.getCause.getMessage.



    // 6. Verify that findById was called with the correct argument


        verifymockUserRepository.findByIdnonExistentUserId.


        // Again, this won't be hit during verification.



// Dummy User class for example same as before

In this example, whenmockUserRepository.findById99L sets up the expectation for a specific call, and thenThrownew UserNotFoundException... dictates the behavior.

This is a very common and readable pattern for testing failure scenarios with value-returning methods.

Throwing Multiple Exceptions Sequentially

Sometimes, you need to simulate a scenario where a mocked method throws an exception on its first invocation, but then perhaps another exception on its second, or even behaves normally on subsequent calls.

Mockito allows you to chain doThrow or thenThrow calls to define a sequence of behaviors.

This is incredibly useful for testing retry mechanisms, circuit breakers, or handling transient errors. Ginkgo testing framework

The Chaining Mechanism

Mockito’s fluent API makes chaining straightforward.

You simply add multiple doThrow or thenThrow calls in sequence.

// For doThrow.when
doThrownew IOException”Disk Full”
.doThrownew SQLException”Connection Lost”

.doReturn"Success" // After two exceptions, it returns success
 .whenmockService.writeToLoganyString.

// For when.thenThrow
whenmockRepository.getData

.thenThrownew TimeoutException"API Timeout"


.thenThrownew ServiceUnavailableException"Service Down"


.thenReturnsomeData. // After two exceptions, it returns data

Scenario: Simulating Transient Network Issues

Imagine a ReportingService that fetches data from an external AnalyticsAPI. You want to test how ReportingService handles a situation where the first few attempts to connect to the AnalyticsAPI fail e.g., due to network glitches or server overloads, but eventually, the connection stabilizes and data is retrieved successfully.

public class ReportingService {
private final AnalyticsAPI analyticsAPI.

public ReportingServiceAnalyticsAPI analyticsAPI {
     this.analyticsAPI = analyticsAPI.



public String generateReportString reportType {
     final int MAX_RETRIES = 3.
     for int i = 0. i < MAX_RETRIES. i++ {
         try {


            System.out.println"Attempt " + i + 1 + " to generate report...".


            return analyticsAPI.fetchDatareportType.
         } catch NetworkException e {


            System.err.println"Transient network error: " + e.getMessage + ". Retrying...".


            // In a real scenario, you might add a small delay here


            // try { Thread.sleep100. } catch InterruptedException ie { Thread.currentThread.interrupt. }
         } catch ServiceException e {


            System.err.println"Service error: " + e.getMessage.


            // For non-transient errors, perhaps break or re-throw immediately


            throw new ReportGenerationException"Service error during report generation", e.
         }


    throw new ReportGenerationException"Failed to generate report after multiple retries.".

// The external API interface
interface AnalyticsAPI {

String fetchDataString reportType throws NetworkException, ServiceException.

// Custom exceptions
class NetworkException extends Exception {

public NetworkExceptionString message { supermessage. }

class ServiceException extends Exception {

public ServiceExceptionString message { supermessage. }

Class ReportGenerationException extends RuntimeException { How to handle dynamic elements in selenium

public ReportGenerationExceptionString message { supermessage. }


public ReportGenerationExceptionString message, Throwable cause { supermessage, cause. }

public class ReportingServiceTest {

void generateReport_shouldSucceedAfterTransientNetworkErrors throws NetworkException, ServiceException {
     // 1. Create a mock for AnalyticsAPI


    AnalyticsAPI mockAnalyticsAPI = mockAnalyticsAPI.class.



    // 2. Configure the mock to throw NetworkException twice, then return a valid result


    // We use when.thenThrow because fetchData returns a String.


    whenmockAnalyticsAPI.fetchDataanyString


        .thenThrownew NetworkException"Connection Reset" // First call


        .thenThrownew NetworkException"Read Timeout"     // Second call


        .thenReturn"Comprehensive Sales Report Data".     // Third call and subsequent calls



    // 3. Create the ReportingService with the mocked API


    ReportingService reportingService = new ReportingServicemockAnalyticsAPI.

     // 4. Call the method under test


    String report = reportingService.generateReport"Sales".

     // 5. Assert the expected outcome
     assertNotNullreport.


    assertEquals"Comprehensive Sales Report Data", report.



    // 6. Verify that fetchData was called three times due to retries


    verifymockAnalyticsAPI, times3.fetchDataeq"Sales".



void generateReport_shouldThrowException_whenPersistentServiceErrorOccurs throws NetworkException, ServiceException {





    // 2. Configure the mock to throw a ServiceException immediately




        .thenThrownew ServiceException"API Key Invalid".








    // 4. Assert that calling generateReport throws the expected ReportGenerationException


    ReportGenerationException thrown = assertThrowsReportGenerationException.class,  -> {


        reportingService.generateReport"Usage".



    // 5. Verify the details of the thrown exception


    assertTruethrown.getMessage.contains"Service error during report generation".


    assertTruethrown.getCause instanceof ServiceException.


    assertEquals"API Key Invalid", thrown.getCause.getMessage.



    // 6. Verify fetchData was called once no retries for this type of error


    verifymockAnalyticsAPI, times1.fetchDataeq"Usage".

This demonstrates how chaining allows you to simulate complex interactions over time, crucial for testing resilient systems.

The ability to simulate transient failures is vital for building robust applications that can withstand real-world imperfections.

Using Argument Matchers with Exception Throws

One of Mockito’s most powerful features is its argument matchers.

These allow you to specify that a mocked method should only exhibit a particular behavior like throwing an exception when it’s called with arguments that meet certain criteria, rather than just any argument.

This precision helps in writing more targeted and effective unit tests.

How Argument Matchers Enhance Exception Testing

Imagine a scenario where a FileProcessor attempts to read a configuration file.

You want to simulate an IOException specifically when the file path points to a non-existent or corrupted file, but not when it points to a valid one.

Argument matchers like anyString, eq, startsWith, argThat, and more, become indispensable here.

Practical Example: Conditional File Access Error

Let’s say you have a ConfigurationManager that reads settings from different files. Write files using fs writefilesync in node js

You want to test its error handling when it tries to read a “corrupt” configuration file, while still allowing it to read “valid” ones.

public class ConfigurationManager {
private final FileReader reader.

public ConfigurationManagerFileReader reader {
     this.reader = reader.



public String loadConfigurationString filePath {


        System.out.println"Attempting to load configuration from: " + filePath.
         return reader.readFilefilePath.
     } catch IOException e {


        System.err.println"Error reading configuration from " + filePath + ": " + e.getMessage.


        throw new ConfigurationLoadingException"Failed to load configuration from " + filePath, e.

// Interface for file reading
interface FileReader {

String readFileString path throws IOException.

// Custom exception

Class ConfigurationLoadingException extends RuntimeException {

public ConfigurationLoadingExceptionString message, Throwable cause {

public class ConfigurationManagerTest {

void loadConfiguration_shouldThrowException_forCorruptFile throws IOException {
     // 1. Create a mock for the FileReader


    FileReader mockFileReader = mockFileReader.class.



    // 2. Configure the mock to throw IOException specifically when the path is "config/corrupt.properties"


    // We use eq to match an exact string argument.


    doThrownew IOException"Permission denied".whenmockFileReader.readFileeq"config/corrupt.properties".



    // 3. Configure a normal return for a valid file to show differentiation


    whenmockFileReader.readFileeq"config/application.properties".thenReturn"app.name=MyApp\napp.version=1.0".




    // 4. Create the ConfigurationManager with the mocked reader


    ConfigurationManager configManager = new ConfigurationManagermockFileReader.

     // Test the corrupt file scenario


    ConfigurationLoadingException thrown = assertThrowsConfigurationLoadingException.class,  -> {


        configManager.loadConfiguration"config/corrupt.properties".



    assertTruethrown.getMessage.contains"Failed to load configuration from config/corrupt.properties".


    assertTruethrown.getCause instanceof IOException.


    assertEquals"Permission denied", thrown.getCause.getMessage.



    // Verify that readFile was called for the corrupt file


    verifymockFileReader.readFileeq"config/corrupt.properties".



    // Test the valid file scenario should not throw an exception


    String validConfig = configManager.loadConfiguration"config/application.properties".
     assertNotNullvalidConfig.
     assertTruevalidConfig.contains"MyApp".



    // Verify that readFile was called for the valid file


    verifymockFileReader.readFileeq"config/application.properties".



    // Verify that readFile was called exactly twice in total


    verifymockFileReader, times2.readFileanyString.



void loadConfiguration_shouldThrowException_forAnyFileEndingWithXml throws IOException {





    // 2. Configure the mock to throw an IOException for any file path ending with ".xml"


    // We use argThat with a custom Hamcrest matcher or a simple lambda.


    doThrownew IOException"XML parsing error".whenmockFileReader


            .readFileargThatpath -> path != null && path.endsWith".xml".

     // 3. Create the ConfigurationManager



     // Test scenario 1: XML file


    ConfigurationLoadingException thrownXml = assertThrowsConfigurationLoadingException.class,  -> {


        configManager.loadConfiguration"data/users.xml".


    assertTruethrownXml.getCause instanceof IOException.


    assertEquals"XML parsing error", thrownXml.getCause.getMessage.


    verifymockFileReader.readFileeq"data/users.xml".



    // Test scenario 2: Non-XML file should not throw, unless configured otherwise


    // By default, un-stubbed methods return default values null for String.


    String jsonConfig = configManager.loadConfiguration"data/settings.json".


    assertNulljsonConfig. // No exception, but also no return value stubbed.


    verifymockFileReader.readFileeq"data/settings.json".

Using argument matchers provides fine-grained control over when your mocks throw exceptions, leading to more precise and meaningful tests.

Remember that when using argument matchers, all arguments for a given method call must be provided by matchers e.g., eq, any, argThat, or Mockito will throw an InvalidUseOfMatchersException.

Best Practices and Common Pitfalls

While Mockito’s exception-throwing capabilities are incredibly powerful, using them effectively requires adherence to certain best practices and awareness of common pitfalls.

Over-mocking, incorrect usage, or not testing edge cases properly can lead to brittle or incomplete tests. Monkey patching

Best Practices

  1. Test the Handling Logic, Not Just the Exception: The primary goal isn’t just to see that an exception is thrown by the mock, but to verify how your code responds to that exception. Does it log the error? Retry the operation? Throw a custom application-specific exception? Display a user-friendly message? Focus your assertions on these outcomes.
  2. Be Specific with Exceptions: Whenever possible, throw the exact exception type e.g., SQLException, IOException, TimeoutException that your real dependency would throw. This ensures your catch blocks are correctly configured and tested.
  3. Use doThrow.when for Consistency and void methods: As discussed, doThrow.when works for both void and non-void methods, making your test setup more consistent. For void methods, it’s the only option.
  4. Leverage Argument Matchers for Precision: Don’t just throw an exception for any call if the real-world scenario only triggers it under specific conditions e.g., specific input, invalid data. Use eq, any, argThat, etc., to make your exception conditions precise.
  5. Chain Behaviors for Complex Scenarios: For transient errors, retries, or state-dependent behavior, use sequential doThrow.doReturn or thenThrow.thenReturn chains to simulate realistic sequences of events.
  6. Verify Interactions: After simulating an exception, always verify that the mocked method was called as expected e.g., verifymock, times1.someMethod or verifymock, atLeastOnce.someMethod. This ensures your test is actually exercising the code path you intend.
  7. Keep Tests Isolated: Ensure that each test method focuses on a single scenario e.g., one specific exception type, one failure path. Avoid combining too many different exception scenarios into one test, which can make it hard to pinpoint failures.
  8. Clean Up: While Mockito handles much of the cleanup, for complex setups, ensure mocks are reset or recreated for each test e.g., using @BeforeEach or @Mock and @InjectMocks with @ExtendWithMockitoExtension.class.

Common Pitfalls

  1. Throwing Checked Exceptions Without try-catch: If a method you’re mocking declares a checked exception, Mockito’s when or doThrow statement will also need to be wrapped in a try-catch block or declared to throw the exception in the test method signature.

    // Bad: Compiler error if MyCheckedException is checked and not handled

    // whenmockService.doSomething.thenThrownew MyCheckedException”Error”.

    // Good: Handle or declare

    Void testCheckedException throws MyCheckedException { // Declared

    whenmockService.doSomething.thenThrownew MyCheckedException"Error".
    

    Void testCheckedExceptionWithCatch { // Handled

        whenmockService.doSomething.thenThrownew MyCheckedException"Error".
     } catch MyCheckedException e {
    
    
        // This block is for compiler, won't be executed at runtime of setup
    
  2. Mixing Matchers and Non-Matchers: If you use any argument matcher e.g., anyString, eq for a method call, all arguments for that call must be matchers. You cannot mix raw values with matchers.

    // Bad: Mixing raw value “id123” with anyString

    // whenmockRepo.find”id123″, anyString.thenThrownew NotFoundException.

    // Good: All arguments are matchers Unit testing php

    WhenmockRepo.findeq”id123″, anyString.thenThrownew NotFoundException.

  3. Mocking Concrete Classes Instead of Interfaces/Abstract Classes: While Mockito can mock concrete classes, it’s generally better practice to mock interfaces or abstract classes. This promotes loose coupling and makes your code more testable. When mocking concrete classes, be aware that final methods cannot be mocked by default without specific Mockito configurations like the mockito-inline artifact.

  4. Over-Mocking/Testing Implementation Details: Don’t mock every single dependency or every internal method. Focus on testing the public behavior and interactions of the unit under test. Mocking too much can lead to brittle tests that break when internal implementation details change, even if the public behavior remains the same. A good heuristic is to mock only direct dependencies that communicate with external systems or have complex logic.

  5. Not Resetting Mocks in specific scenarios: If you’re using the same mock instance across multiple tests in a way that its state carries over, you might need to use Mockito.resetmock to clear its previous stubs and verifications. However, using @Mock and @BeforeEach or @Rule for JUnit 4 typically handles this for you by providing a fresh mock for each test.

  6. Forgetting verify: Stubbing an exception only defines what should happen. You still need to verify that the method was actually called with the correct arguments to ensure your code took the path you expected.

By understanding and applying these best practices while avoiding common pitfalls, you can write effective, robust, and maintainable unit tests that thoroughly cover the exception handling logic of your applications.

Integrating with JUnit 5 Assertions for Exceptions

JUnit 5 provides powerful and expressive assertion methods, particularly assertThrows and assertDoesNotThrow, which are perfectly suited for testing exception handling in conjunction with Mockito.

These methods make your tests cleaner, more readable, and less prone to errors compared to traditional try-catch blocks for asserting exceptions.

assertThrowsexpectedType, executable

This is the cornerstone for testing that a specific exception is thrown.

  • expectedType: The class of the exception you expect to be thrown e.g., MyCustomException.class, IOException.class.
  • executable: A lambda expression Executable functional interface representing the code block that is expected to throw the exception.

assertThrows returns the actual exception instance that was thrown, allowing you to perform further assertions on its message, cause, or other properties. Browserstack newsletter january 2025

assertDoesNotThrowexecutable

This is useful for explicitly asserting that a code block does not throw any exception. While less common for direct exception testing, it can be valuable in scenarios where you’ve configured some mocks to throw, but for a specific set of inputs, you expect normal execution.

Example: Validating User Input with Assertions

Let’s say you have a UserService that validates user input before proceeding.

If validation fails, it should throw a ValidationException.

 private final Validator validator.

 public UserServiceValidator validator {
     this.validator = validator.



public User registerUserString username, String password {


    // Validate inputs using the external validator
         validator.validateUsernameusername.
         validator.validatePasswordpassword.
     } catch ValidationException e {


        // Re-throw a more specific business exception or handle


        throw new RegistrationFailedException"Validation error during registration: " + e.getMessage, e.



    // If validation passes, proceed with user creation mocked here
     return new Userusername, password.

// Validator interface
interface Validator {

void validateUsernameString username throws ValidationException.


void validatePasswordString password throws ValidationException.

// Custom validation exception
class ValidationException extends Exception {
public ValidationExceptionString message {

// Custom registration failed exception

Class RegistrationFailedException extends RuntimeException {

public RegistrationFailedExceptionString message, Throwable cause {

// Dummy User class
class User {
String username.
String password.

public UserString username, String password {
     this.username = username.
     this.password = password.
 // Getters etc.

public class UserServiceJUnit5Test {

void registerUser_shouldThrowRegistrationFailedException_whenUsernameValidationFails throws ValidationException {
     // 1. Create a mock for the Validator


    Validator mockValidator = mockValidator.class.



    // 2. Configure the mock to throw ValidationException when validateUsername is called with "short"


    doThrownew ValidationException"Username too short".whenmockValidator.validateUsernameeq"short".



    // Configure other validation methods to do nothing for this test


    doNothing.whenmockValidator.validatePasswordanyString.



    // 3. Create the UserService with the mocked validator


    UserService userService = new UserServicemockValidator.



    // 4. Use assertThrows to verify the exception


    RegistrationFailedException thrown = assertThrowsRegistrationFailedException.class,  -> {


        userService.registerUser"short", "securepass".



    // 5. Assert details of the thrown exception


    assertTruethrown.getMessage.contains"Validation error during registration".


    assertTruethrown.getCause instanceof ValidationException.


    assertEquals"Username too short", thrown.getCause.getMessage.



    // 6. Verify that validateUsername was called


    verifymockValidator.validateUsernameeq"short".


    // And validatePassword was not called in this flow


    verifymockValidator, never.validatePasswordanyString.



void registerUser_shouldRegisterUser_whenValidationPasses throws ValidationException {





    // 2. Configure the mock to do nothing i.e., pass validation for any username/password


    doNothing.whenmockValidator.validateUsernameanyString.










    // 4. Use assertDoesNotThrow to verify no exception is thrown


    User registeredUser = assertDoesNotThrow -> {


        return userService.registerUser"validuser", "strongpassword".

     // 5. Assert the user was created
     assertNotNullregisteredUser.


    assertEquals"validuser", registeredUser.username.



    // 6. Verify validation methods were called


    verifymockValidator.validateUsernameeq"validuser".


    verifymockValidator.validatePasswordeq"strongpassword".

Using assertThrows and assertDoesNotThrow simplifies your exception tests, making them more readable and robust. What is devops

They are the recommended way to handle exception assertions in modern JUnit 5 tests.

Mockito and Checked vs. Unchecked Exceptions

Understanding the distinction between checked and unchecked exceptions is fundamental when working with Mockito to simulate errors.

This distinction impacts how you configure your mocks and how your application code handles the exceptions.

Checked Exceptions

  • Definition: These are exceptions that must be caught or declared in the method signature i.e., listed in the throws clause of a method. They typically represent predictable but unrecoverable problems, such as IOException, SQLException, ClassNotFoundException.

  • Mockito Impact: When you configure a mock to throw a checked exception, the when.thenThrow or doThrow.when statement itself will need to be wrapped in a try-catch block or declared to throw that checked exception in your test method’s signature. This is because the Java compiler enforces the handling of checked exceptions at compile time.
    // Example: Checked exception in test setup

    Void testMethodThrowsCheckedException throws SQLException { // Test method declares it

    doThrownew SQLException"DB Down".whenmockService.connect.
     // ... test logic ...
    

    Void testMethodThrowsCheckedExceptionWithTryCatch {
    try { // Test method handles it

    doThrownew SQLException”DB Down”.whenmockService.connect.

    // This catch block is for the compiler and will not be executed during setup

    fail”Should not reach here – setup is for throwing, not catching”. Etl test

  • Application Code Handling: Your application code that calls the mocked method must either catch the checked exception or declare it in its own throws clause. If it doesn’t, the code won’t compile.

Unchecked Exceptions Runtime Exceptions

  • Definition: These are exceptions that do not need to be caught or declared. They usually represent programming errors or unexpected conditions, such as NullPointerException, IllegalArgumentException, ArrayIndexOutOfBoundsException, or custom RuntimeExceptions.

  • Mockito Impact: When you configure a mock to throw an unchecked exception, you do not need to wrap the when.thenThrow or doThrow.when statement in a try-catch block, nor do you need to declare it in your test method’s signature. The compiler does not enforce their handling at compile time.
    // Example: Unchecked exception in test setup

    Void testMethodThrowsUncheckedException { // No throws clause needed

    doThrownew IllegalArgumentException"Invalid input".whenmockService.processanyString.
    
  • Application Code Handling: Your application code can choose to catch unchecked exceptions, but it’s not strictly required by the compiler. Often, unchecked exceptions are allowed to propagate up the call stack to be handled by a global exception handler or to crash the application if they represent a fundamental programming error.

When to Use Which in Your Application

  • Checked Exceptions: Use for situations where a calling method can reasonably recover or where the failure is an expected external condition. For instance, if a file might not exist FileNotFoundException or a network connection might fail SocketException. These force the developer to consider the error.
  • Unchecked Exceptions: Use for programming errors NullPointerException, IllegalArgumentException for invalid internal state or for situations where the error is truly unrecoverable at the current level and should propagate up to a higher-level error handler e.g., a web framework’s exception resolver. Overusing checked exceptions can lead to “catch-and-do-nothing” blocks or bloated throws clauses, making code harder to maintain. Many modern applications lean towards unchecked exceptions for business logic errors and use checked exceptions primarily for specific I/O or inter-process communication failures.

Example Demonstrating Both

// Our dependency interface
interface PaymentGateway {

void processPaymentdouble amount throws PaymentGatewayException, NetworkConnectionException.

// Custom checked exception e.g., specific to the payment gateway’s business rules
class PaymentGatewayException extends Exception {

public PaymentGatewayExceptionString message { supermessage. }

// Custom checked exception e.g., for network issues

Class NetworkConnectionException extends Exception {

public NetworkConnectionExceptionString message { supermessage. }

// Custom unchecked exception e.g., for invalid input from caller, programming error Download safari windows

Class InvalidPaymentAmountException extends RuntimeException {

public InvalidPaymentAmountExceptionString message { supermessage. }

// Service under test
public class OrderService {
private final PaymentGateway paymentGateway.

public OrderServicePaymentGateway paymentGateway {
     this.paymentGateway = paymentGateway.

 public boolean placeOrderdouble amount {
     if amount <= 0 {


        throw new InvalidPaymentAmountException"Payment amount must be positive.". // Unchecked
         paymentGateway.processPaymentamount.
         return true.
     } catch PaymentGatewayException e {


        System.err.println"Payment gateway error: " + e.getMessage.
         return false.

// Could log and return false, or re-throw a business exception
} catch NetworkConnectionException e {

        System.err.println"Network connection error: " + e.getMessage.


        // This might trigger a retry mechanism in a real app, or throw a higher-level exception


        throw new RuntimeException"Network issue during payment processing", e. // Re-throwing as unchecked

public class OrderServiceTest {

void placeOrder_shouldReturnFalse_whenPaymentGatewayThrowsPaymentGatewayException throws NetworkConnectionException {


    PaymentGateway mockGateway = mockPaymentGateway.class.



    // Checked exception setup needs handling by compiler


        doThrownew PaymentGatewayException"Insufficient Funds".whenmockGateway.processPaymentanyDouble.
    } catch PaymentGatewayException e { /* This won't run */ }



    OrderService orderService = new OrderServicemockGateway.


    boolean result = orderService.placeOrder100.0.

     assertFalseresult.
     verifymockGateway.processPayment100.0.



void placeOrder_shouldThrowRuntimeException_whenPaymentGatewayThrowsNetworkConnectionException throws PaymentGatewayException {







        doThrownew NetworkConnectionException"API Timeout".whenmockGateway.processPaymentanyDouble.
    } catch NetworkConnectionException e { /* This won't run */ }






    RuntimeException thrown = assertThrowsRuntimeException.class,  -> {
         orderService.placeOrder50.0.



    assertTruethrown.getMessage.contains"Network issue".


    assertTruethrown.getCause instanceof NetworkConnectionException.
     verifymockGateway.processPayment50.0.



void placeOrder_shouldThrowInvalidPaymentAmountException_whenAmountIsZero {


    PaymentGateway mockGateway = mockPaymentGateway.class. // This mock won't be used for this test case





    // Unchecked exception setup needs no special handling


    InvalidPaymentAmountException thrown = assertThrowsInvalidPaymentAmountException.class,  -> {
         orderService.placeOrder0.0.



    assertEquals"Payment amount must be positive.", thrown.getMessage.


    // Verify no interaction with mockGateway because the exception occurs before its call
     verifyNoInteractionsmockGateway.

This example clarifies how Mockito setups differ for checked vs. unchecked exceptions and reiterates the importance of robust exception handling in your actual application code.

Troubleshooting Common Mockito Exception Issues

Even with a clear understanding of Mockito’s exception-throwing mechanisms, you might occasionally encounter issues.

Here’s a rundown of common problems and how to troubleshoot them, ensuring your tests are robust and reliable.

1. InvalidUseOfMatchersException

Problem: You’re mixing argument matchers like anyString, eq with raw literal values for a single method call when configuring your mock.

// BAD: Mixing “userId” literal with anyString matcher

// whenmockService.findUser”userId”, anyString.thenReturnuser. Module css

Solution: If you use any argument matcher for a method call, all arguments for that call must be argument matchers.

// GOOD: All arguments are matchers

WhenmockService.findUsereq”userId”, anyString.thenReturnuser.
// Or, if you want to match any userId:

WhenmockService.findUseranyString, anyString.thenReturnuser.

2. Mocked Method Not Throwing Exception Returning Default Value

Problem: You’ve configured your mock to throw an exception, but when you call the method in your test, it returns null for objects, 0 for numbers, false for booleans, or does nothing for void methods.

Possible Causes & Solutions:

  • Wrong Method Signature: You’re stubbing a method with arguments that don’t precisely match the method being called in your test.

    • Solution: Double-check the arguments. Are they the correct types? Are they in the correct order? If you’re using eq, is the value truly equal?
      // Stubbing:

    WhenmockRepo.findByIdeq1L.thenThrownew NotFoundException.
    // Calling in test:

    // User foundUser = service.findById1. // This might work if 1 is auto-boxed to Long

    // User foundUser = service.findById1L. // This is more precise Browserstack bitrise partnership

    // User foundUser = service.findById2L. // This will NOT trigger the exception, returns null

  • Method Called Before Stubbing: The method on the mock is being invoked by your code before you’ve set up the when.thenThrow or doThrow.when rule.

    • Solution: Ensure all when/doThrow statements are executed before the code under test calls the mocked method. This usually means setting up mocks in @BeforeEach or at the start of your test method.
  • Private/Final Methods: Mockito generally cannot mock private or final methods by default without mockito-inline artifact. If the method you’re trying to make throw an exception is private or final, Mockito will ignore your stubbing and execute the real method.

    • Solution: Prefer mocking interfaces or abstract classes. For concrete classes, if you absolutely must mock final methods, add mockito-inline to your pom.xml or build.gradle. For private methods, consider refactoring your code to make the logic testable through public methods, or test the public method that calls the private one.
  • Not a Mocked Object: You’re calling the method on a real object, not the Mockito mock.

    • Solution: Verify that the instance your service under test is using is indeed the mock you configured. This is often the case if you forget @InjectMocks or to manually inject the mock.

3. NullPointerException Unexpectedly in Test Setup

Problem: Your when.thenThrow or doThrow.when line itself throws a NullPointerException.

  • Mock Object is null: The mock call itself failed, or you forgot to initialize the mock.
    • Solution: Ensure your mock objects are properly initialized, either manually MyService mock = mockMyService.class. or using @Mock with MockitoAnnotations.openMocksthis or @ExtendWithMockitoExtension.class.

4. UnnecessaryStubbingException

Problem: Mockito detects that you’ve configured a stub like thenThrow for a method call that never actually happened during the test execution.

Solution: This is Mockito’s way of telling you that you might have dead code in your test, or that your test isn’t hitting the expected code path.

  • If truly unnecessary: Remove the stubbing.
  • If expected to be called: Verify why the mocked method isn’t being invoked. Is there a conditional statement in your code that’s preventing it? Are the arguments mismatched, causing it to look for a different stub? Use verifymock, never.someMethod to confirm it was not called.
  • Suppress for specific cases use with caution: If you genuinely need a stub that might not always be invoked e.g., in a shared setup method, you can suppress this warning using @DoNotMock or by configuring Mockito’s strictness Mockito.lenient.when.... However, suppressing it should be a conscious decision, as it hides potential issues.

5. MockitoException: Only 'void' methods can be stubbed with doThrow.when.

Problem: You’re trying to use doThrow.when on a method that returns a value.

Solution: This error message is a bit misleading. doThrow.when can be used with value-returning methods. The error usually means you’ve tried to put the method call itself inside the when part of doThrow.when for a method that returns a value. Remember the structure for doThrow.when is doThrowexception.whenmockObject.methodCallargs..

// BAD: Incorrect syntax for doThrow.when with a value-returning method Sdlc tools

// doThrownew IOException”Error”.whenmockService.getData. // Compiler error here

// GOOD: Correct syntax for doThrow.when with a value-returning method

DoThrownew IOException”Error”.whenmockService.getData.

The error message often comes from whenmockService.getData where getData returns a value, and then trying to chain doThrow instead of thenThrow.

By systematically checking these common issues, you can efficiently troubleshoot most Mockito exception-related problems and get your tests back on track.

Conclusion and Further Learning

Mastering Mockito’s exception-throwing capabilities is a crucial skill for any Java developer engaged in unit testing.

It empowers you to thoroughly test the resilience and error-handling mechanisms of your applications, moving beyond mere “happy path” testing to validate how your code behaves under adverse conditions.

By effectively simulating database failures, network timeouts, validation errors, and other exceptional scenarios, you build more robust and reliable software.

We’ve explored the core patterns:

  • doThrow.when: The versatile choice, especially for void methods, and often preferred for consistency across all method types.
  • when.thenThrow: The fluent choice for value-returning methods.
  • Sequential Throwing: For simulating transient errors and retries.
  • Argument Matchers: For precise control over when an exception is thrown.
  • JUnit 5 Assertions: For clean and readable exception assertions.
  • Checked vs. Unchecked Exceptions: Understanding the compiler’s role and how it affects your test setup.

Remember, the goal isn’t just to make a mock throw an exception, but to test your application’s response to that exception. Does it log correctly? Does it provide a meaningful error to the user? Does it gracefully recover or re-throw a more appropriate business-level exception? These are the questions your tests should answer. Eclipse testing

As a Muslim professional, we are always encouraged to strive for excellence Ihsan in all our endeavors, and this includes our professional work in software development.

Building robust and reliable systems, especially those that handle sensitive information or critical operations, is a form of trust Amanah. Thorough testing, including comprehensive exception handling, ensures that the systems we build are dependable, resilient, and less likely to cause unforeseen issues for their users.

It reflects a commitment to quality and attention to detail, which are deeply valued principles.

Further Learning and Resources:

  • Official Mockito Documentation: The ultimate source for comprehensive information, examples, and the latest features. https://site.mockito.org/
  • JUnit 5 Documentation: Dive deeper into JUnit 5’s assertion library, including assertThrows and assertDoesNotThrow. https://junit.org/junit5/docs/current/user-guide/
  • Hamcrest Matchers: Mockito often uses Hamcrest matchers especially with argThat. Understanding Hamcrest can unlock more powerful custom argument matching.
  • Refactoring for Testability: Explore design patterns and principles that make your code inherently easier to test with mocks, such as dependency injection and single responsibility principle.

By continuously refining your testing skills and applying these principles, you contribute to building software that is not only functional but also trustworthy and resilient, reflecting a commitment to excellence in your craft.

Frequently Asked Questions

What is Mockito used for in testing?

Mockito is a popular open-source mocking framework for Java.

It’s used in unit testing to create mock objects for dependencies that a class under test relies on, allowing developers to isolate the unit being tested and control its behavior, including simulating various scenarios like throwing exceptions, returning specific values, or verifying method calls.

Why would I want a mock to throw an exception?

You’d want a mock to throw an exception to simulate failure conditions in your application’s dependencies e.g., a database connection error, a network timeout, an invalid input from an external service. This allows you to test how your code under test handles these exceptional situations, ensuring it logs errors, retries operations, or provides appropriate feedback without needing actual external systems to fail.

What is the difference between doThrow.when and when.thenThrow?

Both methods are used to make a mock throw an exception.

  • doThrow.whenmock.methodCallargs: This is the more versatile form. It must be used for void methods. It can also be used for methods that return a value, and is often preferred for consistency and when chaining multiple behaviors like doThrow.doReturn.
  • whenmock.methodCallargs.thenThrowexception: This is typically used for methods that return a value. It’s often considered more readable when configuring a single exception for a value-returning method, as it follows the “when this happens, then do this” syntax.

Can I make a void method throw an exception in Mockito?

Yes, you can. You must use the doThrow.when syntax for void methods. For example: doThrownew IOException"File not found".whenmockWriter.writeanyString..

How do I make a mock throw multiple exceptions sequentially?

Yes, you can chain doThrow or thenThrow calls to make a mock throw different exceptions on successive calls.

For example: doThrownew NetworkException.doThrownew TimeoutException.whenmockApi.call.. The first call to call will throw NetworkException, the second will throw TimeoutException, and subsequent calls will continue to throw TimeoutException or whatever the last chained behavior was.

How do I use argument matchers with doThrow?

You use argument matchers e.g., anyString, eq, argThat in the when part of the doThrow.when or when.thenThrow setup, just like you would for normal stubbing.

For example: doThrownew IllegalArgumentException.whenmockService.processeq"invalid-input". or whenmockRepo.findByIdanyLong.thenThrownew EntityNotFoundException..

What is InvalidUseOfMatchersException and how do I fix it?

This exception occurs when you mix raw literal values with argument matchers in a single method call when configuring a mock. For example, whenmockService.doSomething"literal", anyString is invalid. To fix it, ensure that all arguments for that method call are argument matchers: whenmockService.doSomethingeq"literal", anyString.

How do I test if my code correctly catches an exception?

You use JUnit 5’s assertThrows method.

This method takes the expected exception type and a lambda expression containing the code that should throw the exception.

It returns the thrown exception instance, allowing you to assert its message or cause.

Example: assertThrowsMyCustomException.class, -> myService.doOperation..

Can Mockito throw a checked exception?

Yes, Mockito can throw checked exceptions.

If the method you are mocking declares a checked exception in its throws clause, then the when.thenThrow or doThrow.when statement in your test setup will also need to be wrapped in a try-catch block or declared to throw that checked exception in your test method’s signature.

Can Mockito throw an unchecked exception RuntimeException?

Yes, Mockito can throw unchecked exceptions.

When configuring a mock to throw an unchecked exception which includes RuntimeException and its subclasses, you do not need to handle it with a try-catch block or declare it in your test method’s throws clause, as unchecked exceptions are not enforced by the compiler.

What happens if I try to use when.thenThrow on a void method?

You will get a compilation error, because the when call expects a return value or at least a non-void method call to proceed with the thenThrow chaining.

For void methods, you must use doThrow.when.

Should I mock final methods to throw exceptions?

By default, Mockito cannot mock final methods.

If you try, your stubbing will be ignored, and the real final method will be called.

To mock final methods, you need to enable the Mockito inline mockmaker by adding the mockito-inline artifact to your project dependencies.

However, it’s generally better design to avoid final methods in classes you intend to mock, or to mock interfaces instead of concrete classes.

How can I verify that a method was called after an exception was thrown?

You use Mockito.verify after the code that calls the mocked method.

The verify call itself does not execute the method again.

It only checks if the method was invoked during the execution of the code under test.

Example: assertThrowsMyException.class, -> service.callMethod. verifymockDependency.logErroranyString..

What is Mockito.lenient used for when dealing with exceptions?

Mockito.lenient.when... is used to suppress UnnecessaryStubbingException warnings.

If you have stubbing that might not always be invoked in a particular test case e.g., in a shared test setup, lenient tells Mockito to ignore that unused stubbing warning.

However, use it sparingly as it can hide genuine issues where a stubbed method isn’t being called as expected.

How do I troubleshoot if my mock isn’t throwing the exception I configured?

  1. Check argument matchers: Ensure all arguments match exactly or are covered by any/eq matchers.
  2. Verify method signature: Confirm the mocked method’s name, arguments, and return type match your stubbing precisely.
  3. Order of operations: Make sure your when.thenThrow or doThrow.when call executes before the code under test calls the mocked method.
  4. Is it truly a mock? Ensure the object being called in your service is indeed the Mockito mock, not a real instance.
  5. Checked vs. Unchecked: For checked exceptions, ensure your test method signature or a try-catch block properly handles the declaration.

Can I make a mock throw an exception only for specific arguments?

Yes, use argument matchers.

For example, to throw an exception only when an id is negative: doThrownew IllegalArgumentException.whenmockRepo.deleteargThatid -> id < 0..

What if my test itself throws an exception during setup?

If your when.thenThrow or doThrow.when line throws an exception e.g., NullPointerException, it usually means your mock object itself is null or incorrectly initialized.

Ensure your mocks are set up using @Mock with @ExtendWithMockitoExtension.class or by manually calling mockMyClass.class.

Should I catch the exception thrown by when.thenThrow if it’s checked?

Yes, for checked exceptions, the Java compiler will require you to either wrap the when.thenThrow or doThrow.when call in a try-catch block or declare the exception in your test method’s throws clause.

The try-catch block in the test setup itself will not execute at runtime. it’s purely for compiler compliance.

Is it possible to throw a custom exception with Mockito?

Yes, absolutely.

You can throw any Exception subclass, including your own custom exceptions.

Just instantiate your custom exception and pass it to thenThrow or doThrow. For example: doThrownew MyBusinessException"Custom Error".whenmockService.performAction..

How can I make a method throw an exception after a certain number of calls?

You can use a combination of thenReturn and thenThrow, or doNothing and doThrow, chained sequentially.

For example: whenmockService.getData .thenReturn"first result" .thenReturn"second result" .thenThrownew RuntimeException"Third call failed".

How useful was this post?

Click on a star to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *