Mockito @SpyBean Annotation

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

  1. SpyBean Annotation:

    @SpyBean
    private NotificationService notificationService;
    

    The @SpyBean annotation creates a spy object for the NotificationService class and injects it into the Spring application context. This spy will override any existing bean of the same type.

  2. Autowired Annotation:

    @Autowired
    private BookService bookService;
    

    The @Autowired annotation injects the BookService bean from the Spring application context. The BookService bean will use the spied NotificationService.

  3. 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‘s sendNotification 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.

Leave a Comment

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

Scroll to Top