Introduction
In this chapter, we will learn about the @SpyBean
annotation in Mockito, specifically within the context of Spring Boot applications. The @SpyBean
annotation is used to create and inject spy objects into the Spring application context. Spies are partial mocks that allow you to call real methods while still being able to stub and verify certain behaviors.
Key Points about the @SpyBean Annotation
- Spring Context Integration: The
@SpyBean
annotation is used to add spies to the Spring application context. - Partial Mocks: Allows real methods to be called while enabling stubbing and verification.
- Overrides Existing Beans: It can override existing beans of the same type in the application context.
- Useful for Integration Tests: Particularly useful for integration tests where you want to partially mock certain dependencies.
- Initialization: Managed by the Spring TestContext framework.
Setting Up Mockito and Spring Boot for @SpyBean Annotation
Ensure that you have the necessary dependencies in your pom.xml
if you are using Maven:
<dependencies>
<!-- Mockito Core dependency -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.2.0</version>
<scope>test</scope>
</dependency>
<!-- Spring Boot Starter Test dependency -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>3.1.2</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- H2 Database dependency -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
Example: Using @SpyBean Annotation
Description
In this example, we will create a BookService
class that depends on a NotificationService
class. The BookService
class will use the NotificationService
class to send notifications when a book is added. We will create an integration test for the BookService
class using Mockito’s @SpyBean
annotation to partially mock the NotificationService
.
Class Under Test: BookService
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class BookService {
private final NotificationService notificationService;
@Autowired
public BookService(NotificationService notificationService) {
this.notificationService = notificationService;
}
public void addBook(Book book) {
// Logic to add the book (omitted for brevity)
notificationService.sendNotification(book);
}
}
Supporting Class: NotificationService
import org.springframework.stereotype.Service;
@Service
public class NotificationService {
public void sendNotification(Book book) {
// Logic to send notification (e.g., send an email)
}
}
Entity Class: Book
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
@Entity
public class Book {
@Id
private Long id;
private String isbn;
private String title;
private String author;
// getters and setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getIsbn() {
return isbn;
}
public void setIsbn(String isbn) {
this.isbn = isbn;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
}
Test Class: BookServiceTest
import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
public class BookServiceTest {
@SpyBean
private NotificationService notificationService;
@Autowired
private BookService bookService;
@Test
void testAddBook_SendsNotification() {
// Arrange
Book book = new Book();
book.setIsbn("1234567890");
book.setTitle("Mockito in Action");
book.setAuthor("John Doe");
doNothing().when(notificationService).sendNotification(any(Book.class));
// Act
bookService.addBook(book);
// Assert
verify(notificationService).sendNotification(book);
}
}
Explanation
-
SpyBean Annotation:
@SpyBean private NotificationService notificationService;
The
@SpyBean
annotation creates a spy object for theNotificationService
class and injects it into the Spring application context. This spy will override any existing bean of the same type. -
Autowired Annotation:
@Autowired private BookService bookService;
The
@Autowired
annotation injects theBookService
bean from the Spring application context. TheBookService
bean will use the spiedNotificationService
. -
Test Method:
@Test void testAddBook_SendsNotification() { // Arrange Book book = new Book(); book.setIsbn("1234567890"); book.setTitle("Mockito in Action"); book.setAuthor("John Doe"); doNothing().when(notificationService).sendNotification(any(Book.class)); // Act bookService.addBook(book); // Assert verify(notificationService).sendNotification(book); }
This test method sets up the spy behavior (Arrange), calls the method under test (Act), and checks the expected results (Assert). It verifies that the
NotificationService
‘ssendNotification
method was called with the correct arguments.
Conclusion
The @SpyBean
annotation in Mockito is used for creating and injecting spy objects into the Spring application context. It allows you to call real methods while still enabling stubbing and verification. In this chapter, we demonstrated how to use the @SpyBean
annotation with a simple BookService
example. This approach helps ensure that your Spring Boot application works correctly with partially mocked dependencies, making your tests more reliable and maintainable.