1. Overview
In this quick tutorial, weβll explore the usage of the Spring Boot @MockBeans annotation.
2. Example Setup
Before we dive in, letβs create a simple ticket validator example weβll use throughout this tutorial:
public class TicketValidator {
private CustomerRepository customerRepository;
private TicketRepository ticketRepository;
public boolean validate(Long customerId, String code) {
customerRepository.findById(customerId)
.orElseThrow(() -> new RuntimeException("Customer not found"));
ticketRepository.findByCode(code)
.orElseThrow(() -> new RuntimeException("Ticket with given code not found"));
return true;
}
}
Here, we defined the validate() method that checks whether a given data exists in the database. It uses CustomerRepository and TicketRepository as dependencies.
Now, letβs examine how to create a test and mock dependencies using Springβs @MockBean and @MockBeans annotations.
3. The @MockBean Annotation
Spring framework provides the @MockBean annotation to mock dependencies for testing purposes. This annotation allows us to define a mocked version of a specific bean. A newly created mock will be added to the Spring ApplicationContext. Consequently, if a bean of the same type already exists, itβll be replaced with the mocked version.
Furthermore, we can use this annotation on a field weβd like to mock or on a test class level.
Using the @MockBean annotation, we can isolate the specific part of the code we want to test by mocking the behavior of its dependent objects.
Now, letβs see the @MockBean in action. Letβs replace an existing CustomerRepository bean with a mock implementation:
class MockBeanTicketValidatorUnitTest {
@MockBean
private CustomerRepository customerRepository;
@Autowired
private TicketRepository ticketRepository;
@Autowired
private TicketValidator ticketValidator;
@Test
void givenUnknownCustomer_whenValidate_thenThrowException() {
String code = UUID.randomUUID().toString();
when(customerRepository.findById(any())).thenReturn(Optional.empty());
assertThrows(RuntimeException.class, () -> ticketValidator.validate(1L, code));
}
}
Here, we annotated the CustomerRepository field with the @MockBean annotation. Spring injects the mock into the field and adds it to the application context.
One thing to keep in mind is that we canβt use the @MockBean annotation to mock a beanβs behavior during the application context refresh.
Furthermore, this annotation is defined as @Repeatable, which allows us to define the same annotation multiple times on the class level:
@MockBean(CustomerRepository.class)
@MockBean(TicketRepository.class)
@SpringBootTest(classes = Application.class)
class MockBeanTicketValidatorUnitTest {
@Autowired
private CustomerRepository customerRepository;
@Autowired
private TicketRepository ticketRepository;
@Autowired
private TicketValidator ticketValidator;
// ...
}
4. The @MockBeans Annotation
Now that weβve discussed the @MockBean annotation, letβs move on to the @MockBeans annotation. Simply put, this annotation represents an aggregation of multiple @MockBean annotations and serves as a container for them.
Additionally, it helps us organize test cases. We can define multiple mocks in the same place, making the test class cleaner and more organized. Moreover, it can be useful when reusing mocked beans across numerous test classes.
We can use the @MockBeans as an alternative to the repeatable @MockBean solution we saw earlier:
@MockBeans({@MockBean(CustomerRepository.class), @MockBean(TicketRepository.class)})
@SpringBootTest(classes = Application.class)
class MockBeansTicketValidatorUnitTest {
@Autowired
private CustomerRepository customerRepository;
@Autowired
private TicketRepository ticketRepository;
@Autowired
private TicketValidator ticketValidator;
// ...
}
Itβs worth noting that we used the @Autowired annotation for the beans we wanted to mock.
Moreover, thereβs no difference in functionality between this approach and defining the @MockBean on each field. However, if weβre using Java 8 or higher, the @MockBeans annotation might seem redundant because Java supports repeatable annotations.
The main idea behind the @MockBeans annotation is to allow developers to specify mock beans in one place.
5. Conclusion
In this short article, we learned how to use the @MockBeans annotation while defining mocks for testing.
To summarize, we can use the @MockBeans annotation to group multiple @MockBean annotations and define all mocks in one place.
