Mockito BDDMockito

Introduction

Behavior-Driven Development (BDD) encourages writing tests in a more natural, readable language that describes the behavior of the application. Mockito provides the BDDMockito class, which allows you to use BDD-style syntax for stubbing and verifying interactions. This tutorial will demonstrate how to use BDDMockito to write more readable and expressive tests.

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 LibraryService class that has a dependency on a BookRepository. Our goal is to test the LibraryService methods using BDDMockito to handle stubbing and verifying interactions.

LibraryService and BookRepository Classes

First, create the Book class, the BookRepository interface, and the LibraryService class.

public class Book {
    private String title;
    private String author;

    // Constructor, getters, and setters
    public Book(String title, String author) {
        this.title = title;
        this.author = author;
    }

    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;
    }
}

public interface BookRepository {
    void saveBook(Book book);
    Book findBookByTitle(String title);
    List<Book> findAllBooks();
}

public class LibraryService {
    private final BookRepository bookRepository;

    public LibraryService(BookRepository bookRepository) {
        this.bookRepository = bookRepository;
    }

    public void addBook(String title, String author) {
        Book book = new Book(title, author);
        bookRepository.saveBook(book);
    }

    public Book getBookByTitle(String title) {
        return bookRepository.findBookByTitle(title);
    }

    public List<Book> getAllBooks() {
        return bookRepository.findAllBooks();
    }
}

JUnit 5 Test Class with BDDMockito

Create a test class for LibraryService 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;

import java.util.Arrays;
import java.util.List;

@ExtendWith(MockitoExtension.class)
public class LibraryServiceTest {

    @Mock
    private BookRepository bookRepository;

    @InjectMocks
    private LibraryService libraryService;

    @Test
    public void testAddBook() {
        // Given
        String title = "Mockito in Action";
        String author = "Ramesh Fadatare";

        // When
        libraryService.addBook(title, author);

        // Then
        then(bookRepository).should().saveBook(any(Book.class));
    }

    @Test
    public void testGetBookByTitle() {
        // Given
        String title = "Mockito in Action";
        Book book = new Book(title, "Ramesh Fadatare");
        given(bookRepository.findBookByTitle(title)).willReturn(book);

        // When
        Book result = libraryService.getBookByTitle(title);

        // Then
        assertNotNull(result);
        assertEquals(title, result.getTitle());
        assertEquals("Ramesh Fadatare", result.getAuthor());
    }

    @Test
    public void testGetAllBooks() {
        // Given
        Book book1 = new Book("Mockito in Action", "Ramesh Fadatare");
        Book book2 = new Book("Effective Java", "Joshua Bloch");
        List<Book> books = Arrays.asList(book1, book2);
        given(bookRepository.findAllBooks()).willReturn(books);

        // When
        List<Book> result = libraryService.getAllBooks();

        // Then
        assertNotNull(result);
        assertEquals(2, result.size());
        assertEquals("Mockito in Action", result.get(0).getTitle());
        assertEquals("Effective Java", result.get(1).getTitle());
    }

    @Test
    public void testAddBookWithCustomMatcher() {
        // Given
        String title = "Mockito in Action";
        String author = "Ramesh Fadatare";

        // When
        libraryService.addBook(title, author);

        // Then
        then(bookRepository).should().saveBook(argThat(book -> book.getTitle().equals("Mockito in Action") && book.getAuthor().equals("Ramesh Fadatare")));
    }
}

Explanation

  1. Creating Mocks with @Mock:

    • The @Mock annotation creates a mock instance of the BookRepository interface. This mock instance can be used to simulate the behavior of the BookRepository in a controlled way.
  2. Injecting Mocks with @InjectMocks:

    • The @InjectMocks annotation injects the mock BookRepository into the LibraryService instance to provide a controlled test environment. This allows the LibraryService methods to be tested in isolation from the actual BookRepository implementation.
  3. Using BDDMockito:

    • given(): The given(bookRepository.findBookByTitle(title)).willReturn(book); method configures the mock BookRepository to return a specific Book object when the findBookByTitle method is called with the specified title.
    • then(): The then(bookRepository).should().saveBook(any(Book.class)); method verifies that the saveBook method was called on the BookRepository with any Book object.
    • argThat(): The then(bookRepository).should().saveBook(argThat(book -> book.getTitle().equals("Mockito in Action") && book.getAuthor().equals("Ramesh Fadatare"))); method verifies that the saveBook method was called on the BookRepository with a Book object that matches the specified title and author.

Additional Scenarios

Scenario: Verifying No Interactions

In this scenario, we will demonstrate how to verify that no interactions occurred with the mock object using BDDMockito.

@Test
public void testNoInteractionsWithBookRepository() {
    // Given
    // No interactions expected

    // When
    // No method calls

    // Then
    then(bookRepository).shouldHaveNoInteractions();
}

Explanation

  1. Verifying No Interactions:
    • The then(bookRepository).shouldHaveNoInteractions(); method verifies that no interactions occurred with the BookRepository mock object. This ensures that the BookRepository was not used during the test.

Scenario: Verifying Method Call Order

In this scenario, we will demonstrate how to verify the order of method calls using BDDMockito.

@Test
public void testMethodCallOrder() {
    // Given
    String title = "Mockito in Action";
    Book book = new Book(title, "Ramesh Fadatare");
    given(bookRepository.findBookByTitle(title)).willReturn(book);

    // When
    libraryService.addBook(title, "Ramesh Fadatare");
    libraryService.getBookByTitle(title);

    // Then
    InOrder inOrder = inOrder(bookRepository);
    then(bookRepository).should(inOrder).saveBook(any(Book.class));
    then(bookRepository).should(inOrder).findBookByTitle(title);
}

Explanation

  1. Verifying Method Call Order:
    • The InOrder inOrder = inOrder(bookRepository); statement creates an InOrder object to verify the order of interactions with the BookRepository mock object.
    • The then(bookRepository).should(inOrder).saveBook(any(Book.class)); and then(bookRepository).should(inOrder).findBookByTitle(title); methods verify that the saveBook method was called before the findBookByTitle method on the BookRepository.

Conclusion

Using BDDMockito in Mockito allows you to write more readable and expressive tests that follow the Behavior-Driven Development (BDD) style. By using given(), then(), and other BDD-style methods, you can handle various scenarios and control the behavior of mock objects. This step-by-step guide demonstrated how to effectively use BDDMockito in your unit tests, covering different scenarios to ensure comprehensive testing of the LibraryService 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