Mockito Exception Throwing for Unit Testing | Learn Java Skills

Imran Shaikh
0

There is a standard coding practice in Java application development to capture the exception using the try-catch block, and there may be some business logic in the catch block that you wish to unit test.


If you are wondering, is there a possibility of using the mockito framework to test the catch block?


Mockito makes it simple to throw an exception when invoking the method.


In this quick lesson, we'll look at how to throw an exception and unit test the catch block. Alternatively, you may test your business logic to examine how exceptions are handled in your application.


toc
Mockito Exception Throwing for Unit Testing thumbnail image

Code Overview


In this lesson, we'll look at how we may throw exceptions on both the void and non-void methods.


As a result, I created the following code to cover both functions: I'll teach you how to throw an exception on the non-void notificationService.sendNotification() method and the void transactionRepository.saveNetBankingTransaction() method.


 package in .learnjavaskills.mockitotutorial.service;

 import in .learnjavaskills.mockitotutorial.repository.TransactionRepository;

 import java.math.BigDecimal;
 import java.util.Objects;
 import java.util.concurrent.TimeUnit;

 public class TransactionService {
    private TransactionRepository transactionRepository;

    private NotificationService notificationService;
    public TransactionService(TransactionRepository transactionRepository, NotificationService notificationService) {
        this.transactionRepository = transactionRepository;
        this.notificationService = notificationService;
    }

    public void netBankingTransaction(String username, String password,
        BigDecimal amount, Long transferAccountNumber) throws InterruptedException {
        boolean isValidUser = authenticateUser(username, password);
        if (isValidUser) {
            boolean isTransferSuccess = transferAmount(transferAccountNumber, username, amount);
            if (isTransferSuccess) {
                // Send notification to user
                try {
                    boolean isNotificationSend = notificationService.sendNotification("toEmail@address.com", "Transaction success for " + amount, "Hi " + username + " \n Successfully transfer amount " + amount);
                } catch (Exception exception) {
                    TimeUnit.SECONDS.sleep(10L);
                    // Re-Trying sending notification
                    boolean isNotificationSend = notificationService.sendNotification("toEmail@address.com", "Transaction success for " + amount, "Hi " + username + " \n Successfully transfer amount " + amount);
                }

                // save net banking transaction
                try {
                    transactionRepository.saveNetBankingTransaction(username, amount);
                } catch (Exception exception) {
                    TimeUnit.SECONDS.sleep(10L);
                    // Re-Trying to save net banking transaction
                    transactionRepository.saveNetBankingTransaction(username, amount);
                }
            }
        }
    }
 }

Non-Void Return Type Method


Let's start by learning how to throw an exception on the non-void return type method. This is as simple as mocking the object and returning some dummy value.


In the following example, I've mock the NotificationService class's sendNotification() method, which is in charge of notifying the user. According to the business logic in the above code, if we find an error while notifying the end user, it will retry notifying them after 10 seconds.


Let's see how we can mock the sendNotification() function to throw an exception and check that the sendNotification() method should be executed twice according to our business logic.


 package in .learnjavaskills.mockitotutorial.service;

 import in .learnjavaskills.mockitotutorial.exception.TransactionException;
 import in .learnjavaskills.mockitotutorial.repository.TransactionRepository;
 import org.assertj.core.api.Assertions;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.*;
 import org.mockito.junit.jupiter.MockitoExtension;

 import java.math.BigDecimal;

 @ExtendWith(MockitoExtension.class)
 class TransactionServiceTest {
    @Mock
    private TransactionRepository transactionRepository;

    @Mock private NotificationService notificationService;

    @InjectMocks
    private TransactionService transactionService;

    @Test
    void testWhenExceptionThrownWileSendingNotification() throws InterruptedException {
        BDDMockito.given(notificationService.sendNotification(ArgumentMatchers.any(), ArgumentMatchers.any(),
                ArgumentMatchers.any()))
            .willThrow(TransactionException.class);

        // verifying excetion is thrown with TransactionException
        Assertions.assertThatThrownBy(() - > transactionService.netBankingTransaction("LearnJavaSkills.in",
                "password", BigDecimal.TEN, 1234567890L))
            .isExactlyInstanceOf(TransactionException.class);

        //  verifying 2 times sendNotification() method should invoke because in catch block also we are re-trying to send notification
        BDDMockito.verify(notificationService, Mockito.times(2))
            .sendNotification(ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any());
    }
 }

Void Return Type Method


Let's look at how we may throw an exception on the void method now. Throwing an exception on the void return type method differs somewhat from the non-void return type method shown in the previous example.


To test the catch block code, we mock transactionRepository to raise a TransactionException when the saveNetBankingTransaction() method is executed.


If you thoroughly examined the code overview, you could have seen that if any errors are encountered during saving the net banking transaction, it must re-try after 10 seconds to save the same transaction.


I also used the BDDMockito.verify() method to validate this situation.


 @Test
 void testWhenExceptionThrownWhileSavingNetBankingTransaction() {
    // mocking transactionRepository to throw TransactionException when saveNetBankingTransaction() invoke
    BDDMockito.doThrow(TransactionException.class)
        .when(transactionRepository)
        .saveNetBankingTransaction(ArgumentMatchers.any(), ArgumentMatchers.any());

    // verifying exception are thrown which mock in the above
    Assertions.assertThatThrownBy(() - > transactionService.netBankingTransaction("LearnJavaSkills.in",
            "password",
            BigDecimal.TEN,
            1234567890L))
        .isExactlyInstanceOf(TransactionException.class);

    // verifying saveNetBankingTransaction method invoke two times as per the business logic
    BDDMockito.verify(transactionRepository, Mockito.times(2))
        .saveNetBankingTransaction(ArgumentMatchers.any(), ArgumentMatchers.any());
 }

How to Throw an Exception with a Message


Mockito allows us to throw exceptions as objects, therefore we can also throw exceptions with messages.


In our earlier example, we may substitute the Exception object for the Exception class.


Non-Void Return Type Method - Throw an Exception with a Message

 @Test
 void testWhenExceptionThrownWileSendingNotification() throws InterruptedException {
    String message = "Unable to send notification to user";
    BDDMockito.given(notificationService.sendNotification(ArgumentMatchers.any(), ArgumentMatchers.any(),
            ArgumentMatchers.any()))
        .willThrow(new TransactionException(message));

    Assertions.assertThatThrownBy(() - > transactionService.netBankingTransaction("LearnJavaSkills.in",
            "password",
            BigDecimal.TEN,
            1234567890L))
        .isExactlyInstanceOf(TransactionException.class)
        .hasMessageContaining(message);

    BDDMockito.verify(notificationService, Mockito.times(2))
        .sendNotification(ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any());
 }

Void Return Type Method - Throw an Exception with a Message

  @Test
 void testWhenExceptionThrownWhileSavingNetBankingTransaction() {
     // mocking transactionRepository to throw TransactionException when saveNetBankingTransaction() invoke
     String message = "unable to save net banking transaction";
     BDDMockito.doThrow(new TransactionException(message))
         .when(transactionRepository)
         .saveNetBankingTransaction(ArgumentMatchers.any(), ArgumentMatchers.any());

     // verifying exception are thrown which mock in the above
     Assertions.assertThatThrownBy(() - > transactionService.netBankingTransaction("LearnJavaSkills.in",
             "password",
             BigDecimal.TEN,
             1234567890L))
         .isExactlyInstanceOf(TransactionException.class)
         .hasMessageContaining(message);

     // verifying saveNetBankingTransaction method invoke two times as per the business logic
     BDDMockito.verify(transactionRepository, Mockito.times(2))
         .saveNetBankingTransaction(ArgumentMatchers.any(), ArgumentMatchers.any());
 }

Stubbing Multiple Calls


Mockito is one of the most powerful Java mocking frameworks. Mockito allows us to stub numerous times with various behaviors, which is highly useful for testing sequential exception handling or forward-thinking behavior.


If you've been paying attention to our code overview, you'll see that we're retrying to send the notification to the enduser if any errors occur at notifying.


With mockito, you may stub this sendNotification() method to throw an exception, and then stub this sendNotification() method again to return values when sendNotification() is executed the second time.


Let's look at how we'll unit test if we're willing to throw an exception on the initial method called and return values on the re-try attempt.


 @Test
 void testWhenExceptionThrownWileSendingNotificationStubMultiCall() throws InterruptedException {
    String message = "Unable to send notification to user";
    BDDMockito.given(notificationService.sendNotification(ArgumentMatchers.any(), ArgumentMatchers.any(),
            ArgumentMatchers.any()))
        .willThrow(new TransactionException(message))
        .willReturn(true);

    transactionService.netBankingTransaction("LearnJavaSkills.in",
        "password",
        BigDecimal.TEN,
        1234567890L);

    BDDMockito.verify(notificationService, Mockito.times(2))
        .sendNotification(ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any());
 }


Conclussion


Mockito is a powerful mocking framework for Java applications. We can use the mockito framework to not only mock objects such that they return the values we want, but we can also throw an exception on method call by mocking an object.

Mockito allows multiple stubbing calls, which implies that if you have numerous calls to that method, you may control the behavior by stubbing all of them at once.(alert-success)

Multiple stubbing works in combination with the sequential method invocation in your business logic. Assume you've called a method three times, the first two times throwing an exception and the third time being requested to return values.


The method will then throw an exception the first two times it is executed in your business logic, and it will return values the third time.


Keep learning and keep growing.

Post a Comment

0 Comments
Post a Comment (0)

#buttons=(Accept !) #days=(20)

Our website uses cookies to enhance your experience. Learn More
Accept !
To Top