«1. Обзор

В этом уроке мы познакомимся с классом AdditionalAnswers Mockito и его методами.

2. Возврат аргументов

Основное назначение класса AdditionalAnswers — возвращать параметры, переданные фиктивному методу.

Например, при обновлении объекта имитируемый метод обычно просто возвращает обновленный объект. Используя методы из AdditionalAnswers, мы можем вместо этого вернуть конкретный параметр, переданный методу в качестве аргумента, в зависимости от его позиции в списке параметров.

Кроме того, AdditionalAnswers имеет разные реализации класса Answer.

Чтобы начать нашу демонстрацию, давайте создадим проект библиотеки.

Во-первых, мы создадим одну простую модель:

public class Book {

    private Long bookId;
    private String title;
    private String author;
    private int numberOfPages;
 
    // constructors, getters and setters

}

Кроме того, нам нужен класс репозитория для поиска книг:

public class BookRepository {
    public Book getByBookId(Long bookId) {
        return new Book(bookId, "To Kill a Mocking Bird", "Harper Lee", 256);
    }

    public Book save(Book book) {
        return new Book(book.getBookId(), book.getTitle(), book.getAuthor(), book.getNumberOfPages());
    }

    public Book selectRandomBook(Book bookOne, Book bookTwo, Book bookThree) {
        List<Book> selection = new ArrayList<>();
        selection.add(bookOne);
        selection.add(bookTwo);
        selection.add(bookThree);
        Random random = new Random();
        return selection.get(random.nextInt(selection.size()));
    }
}

Соответственно, у нас есть сервисный класс, который вызывает методы нашего репозитория: ~ ~~

public class BookService {
    private final BookRepository bookRepository;

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

    public Book getByBookId(Long id) {
        return bookRepository.getByBookId(id);
    }

    public Book save(Book book) {
        return bookRepository.save(book);
    }

    public Book selectRandomBook(Book book1, Book book2, Book book3) {
        return bookRepository.selectRandomBook(book1, book2, book3);
    }
}

Имея это в виду, давайте создадим несколько тестов.

2.1. Возврат первого аргумента

Для нашего тестового класса нам нужно включить использование аннотаций с тестами Mockito, аннотировав тестовый класс JUnit для запуска с MockitoJUnitRunner. Кроме того, нам нужно смоделировать наш сервис и класс репозитория:

@RunWith(MockitoJUnitRunner.class)
public class BookServiceUnitTest {
    @InjectMocks
    private BookService bookService;

    @Mock
    private BookRepository bookRepository;

    // test methods

}

Во-первых, давайте создадим тест, возвращающий первый аргумент – AdditionalAnswers.returnsFirstArg():

@Test
public void givenSaveMethodMocked_whenSaveInvoked_ThenReturnFirstArgument_UnitTest() {
    Book book = new Book("To Kill a Mocking Bird", "Harper Lee", 256);
    Mockito.when(bookRepository.save(any(Book.class))).then(AdditionalAnswers.returnsFirstArg());

    Book savedBook = bookService.save(book);

    assertEquals(savedBook, book);
}

Другими словами, мы смоделируем метод сохранения из нашего класса BookRepository, который принимает объект Book.

Когда мы запустим этот тест, он действительно вернет первый аргумент, который равен сохраненному нами объекту Book.

2.2. Возврат второго аргумента

Во-вторых, мы создаем тест, используя AdditionalAnswers.returnsSecondArg():

@Test
public void givenCheckifEqualsMethodMocked_whenCheckifEqualsInvoked_ThenReturnSecondArgument_UnitTest() {
    Book book1 = new Book(1L, "The Stranger", "Albert Camus", 456);
    Book book2 = new Book(2L, "Animal Farm", "George Orwell", 300);
    Book book3 = new Book(3L, "Romeo and Juliet", "William Shakespeare", 200);

    Mockito.when(bookRepository.selectRandomBook(any(Book.class), any(Book.class),
      any(Book.class))).then(AdditionalAnswers.returnsSecondArg());

    Book secondBook = bookService.selectRandomBook(book1, book2, book3);

    assertEquals(secondBook, book2);
}

В этом случае, когда наш метод selectRandomBook выполняется, он вернет вторую книгу.

2.3. Возврат последнего аргумента

Точно так же мы можем использовать AdditionalAnswers.returnsLastArg() для получения последнего аргумента, который мы передали нашему методу:

@Test
public void givenCheckifEqualsMethodMocked_whenCheckifEqualsInvoked_ThenReturnLastArgument_UnitTest() {
    Book book1 = new Book(1L, "The Stranger", "Albert Camus", 456);
    Book book2 = new Book(2L, "Animal Farm", "George Orwell", 300);
    Book book3 = new Book(3L, "Romeo and Juliet", "William Shakespeare", 200);

    Mockito.when(bookRepository.selectRandomBook(any(Book.class), any(Book.class), 
      any(Book.class))).then(AdditionalAnswers.returnsLastArg());

    Book lastBook = bookService.selectRandomBook(book1, book2, book3);
    assertEquals(lastBook, book3);
}

Здесь вызванный метод вернет третью книгу, так как это последний параметр.

2.4. Возврат аргумента по индексу

Наконец, давайте напишем тест, используя метод, который позволяет нам возвращать аргумент по заданному индексу — AdditionalAnswers.returnsArgAt(int index):

@Test
public void givenCheckifEqualsMethodMocked_whenCheckifEqualsInvoked_ThenReturnArgumentAtIndex_UnitTest() {
    Book book1 = new Book(1L, "The Stranger", "Albert Camus", 456);
    Book book2 = new Book(2L, "Animal Farm", "George Orwell", 300);
    Book book3 = new Book(3L, "Romeo and Juliet", "William Shakespeare", 200);

    Mockito.when(bookRepository.selectRandomBook(any(Book.class), any(Book.class), 
      any(Book.class))).then(AdditionalAnswers.returnsArgAt(1));

    Book bookOnIndex = bookService.selectRandomBook(book1, book2, book3);

    assertEquals(bookOnIndex, book2);
}

В конце концов, поскольку мы запросили аргумент из индекса 1, мы получим второй аргумент — в данном случае, book2.

3. Заключение

В целом, в этом руководстве были рассмотрены методы класса AdditionalAnswers Mockito.

Реализация этих примеров и фрагментов кода доступна на GitHub.