🎓 Top 15 Udemy Courses (80-90% Discount): My Udemy Courses - Ramesh Fadatare — All my Udemy courses are real-time and project oriented courses.
▶️ Subscribe to My YouTube Channel (178K+ subscribers): Java Guides on YouTube
▶️ For AI, ChatGPT, Web, Tech, and Generative AI, subscribe to another channel: Ramesh Fadatare on YouTube
Introduction
BDDMockito.willThrow() is a method provided by Mockito to support the Behavior-Driven Development (BDD) style of writing tests. It is used to specify that a method call on a mock object should throw an exception. This is particularly useful when you want to test how your code handles exceptions from dependencies. This tutorial will demonstrate how to use BDDMockito.willThrow() to mock exceptions in a BDD style.
Maven Dependencies
To use Mockito with JUnit 5 and enable BDDMockito syntax, add the following dependencies to your pom.xml file:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>4.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.9.2</version>
<scope>test</scope>
</dependency>
Example Scenario
We will create a PaymentService class that has a dependency on a TransactionRepository. Our goal is to test the PaymentService methods using BDDMockito.willThrow() to handle exceptions.
PaymentService and TransactionRepository Classes
First, create the Transaction class, the TransactionRepository interface, and the PaymentService class.
public class Transaction {
private String id;
private double amount;
// Constructor, getters, and setters
public Transaction(String id, double amount) {
this.id = id;
this.amount = amount;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public double getAmount() {
return amount;
}
public void setAmount(double amount) {
this.amount = amount;
}
}
public interface TransactionRepository {
void saveTransaction(Transaction transaction) throws Exception;
Transaction findTransactionById(String id) throws Exception;
}
public class PaymentService {
private final TransactionRepository transactionRepository;
public PaymentService(TransactionRepository transactionRepository) {
this.transactionRepository = transactionRepository;
}
public void processPayment(String id, double amount) throws Exception {
Transaction transaction = new Transaction(id, amount);
transactionRepository.saveTransaction(transaction);
}
public Transaction getTransactionById(String id) throws Exception {
return transactionRepository.findTransactionById(id);
}
}
JUnit 5 Test Class with BDDMockito
Create a test class for PaymentService using JUnit 5 and BDDMockito.
import static org.mockito.BDDMockito.*;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.junit.jupiter.api.extension.ExtendWith;
@ExtendWith(MockitoExtension.class)
public class PaymentServiceTest {
@Mock
private TransactionRepository transactionRepository;
@InjectMocks
private PaymentService paymentService;
@Test
public void testProcessPaymentThrowsException() {
// Given
String id = "123";
double amount = 100.0;
willThrow(new RuntimeException("Transaction failed")).given(transactionRepository).saveTransaction(any(Transaction.class));
// When & Then
RuntimeException exception = assertThrows(RuntimeException.class, () -> {
paymentService.processPayment(id, amount);
});
assertEquals("Transaction failed", exception.getMessage());
}
@Test
public void testGetTransactionByIdThrowsException() {
// Given
String id = "123";
willThrow(new RuntimeException("Transaction not found")).given(transactionRepository).findTransactionById(id);
// When & Then
RuntimeException exception = assertThrows(RuntimeException.class, () -> {
paymentService.getTransactionById(id);
});
assertEquals("Transaction not found", exception.getMessage());
}
}
Explanation
Creating Mocks with
@Mock:- The
@Mockannotation creates a mock instance of theTransactionRepositoryinterface. This mock instance can be used to simulate the behavior of theTransactionRepositoryin a controlled way.
- The
Injecting Mocks with
@InjectMocks:- The
@InjectMocksannotation injects the mockTransactionRepositoryinto thePaymentServiceinstance to provide a controlled test environment. This allows thePaymentServicemethods to be tested in isolation from the actualTransactionRepositoryimplementation.
- The
Using BDDMockito:
willThrow(): ThewillThrow(new RuntimeException("Transaction failed")).given(transactionRepository).saveTransaction(any(Transaction.class));method configures the mockTransactionRepositoryto throw aRuntimeExceptionwhen thesaveTransactionmethod is called with anyTransactionobject. This allows theprocessPaymentmethod of thePaymentServiceclass to be tested with controlled exception handling behavior.
Verifying Exceptions:
- The
assertThrows(RuntimeException.class, () -> { ... });method verifies that theprocessPaymentandgetTransactionByIdmethods throw aRuntimeExceptionwith the specified message when thesaveTransactionandfindTransactionByIdmethods are called, respectively.
- The
Additional Scenarios
Scenario: Handling Specific Exceptions
In this scenario, we will demonstrate how to handle specific exceptions using BDDMockito.willThrow().
@Test
public void testProcessPaymentThrowsSpecificException() {
// Given
String id = "123";
double amount = 100.0;
willThrow(new IllegalArgumentException("Invalid transaction ID")).given(transactionRepository).saveTransaction(any(Transaction.class));
// When & Then
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
paymentService.processPayment(id, amount);
});
assertEquals("Invalid transaction ID", exception.getMessage());
}
Explanation
- Handling Specific Exceptions with BDDMockito:
- The
willThrow(new IllegalArgumentException("Invalid transaction ID")).given(transactionRepository).saveTransaction(any(Transaction.class));method configures the mockTransactionRepositoryto throw anIllegalArgumentExceptionwhen thesaveTransactionmethod is called with anyTransactionobject. This allows theprocessPaymentmethod of thePaymentServiceclass to be tested with controlled exception handling behavior.
- The
Scenario: Verifying No More Interactions
In this scenario, we will demonstrate how to verify that no more interactions occurred with the mock object using BDDMockito.
@Test
public void testNoMoreInteractionsWithTransactionRepository() {
// Given
String id = "123";
double amount = 100.0;
// When
paymentService.processPayment(id, amount);
// Then
then(transactionRepository).should().saveTransaction(any(Transaction.class));
then(transactionRepository).shouldHaveNoMoreInteractions();
}
Explanation
- Verifying No More Interactions:
- The
then(transactionRepository).shouldHaveNoMoreInteractions();method verifies that no more interactions occurred with theTransactionRepositorymock object after thesaveTransactionmethod was called. This ensures that no unintended interactions happened with theTransactionRepository.
- The
Conclusion
Using BDDMockito.willThrow() in Mockito allows you to write more readable and expressive tests that follow the Behavior-Driven Development (BDD) style. By using willThrow() for stubbing methods to throw exceptions, you can handle various scenarios and control the behavior of mock objects. This step-by-step guide demonstrated how to effectively use BDDMockito.willThrow() in your unit tests, covering different scenarios to ensure comprehensive testing of the PaymentService class.
Related Mockito BDDMockito Class Methods (Behavior-Driven Development Style)
Mockito BDDMockito
Mockito BDDMockito given()
Mockito BDDMockito willThrow()
Mockito BDDMockito willAnswer()
Mockito BDDMockito willReturn()
Mockito BDDMockito willDoNothing()
Mockito BDDMockito willCallRealMethod()
Mockito BDDMockito then()
Mockito BDDMockito.any()
Mockito BDDMockito.times()
Comments
Post a Comment
Leave Comment