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
предлагает краткий и более аккуратный способ создания ответов из функциональных интерфейсов . Для этого он предоставляет два удобных метода: answer() и
answerVoid().
Итак, давайте спустимся в кроличью нору и посмотрим, как использовать их на практике.
Пожалуйста, имейте в виду, что эти два метода помечены @Incubating
. Это означает, что они могут быть изменены позже в зависимости от отзывов сообщества.
3.1. Использование AdditionalAnswers.answer()
Этот метод в основном используется для создания строго типизированного ответа в Java 8 с использованием функционального интерфейса.
Как правило, Mockito поставляется с набором готовых к использованию универсальных интерфейсов, которые мы можем использовать для настройки ответа макета. Например, он предоставляет Answer1<T,A0>
для вызова одного аргумента .
Теперь давайте проиллюстрируем, как использовать AdditionalAnswers.answer()
для создания ответа, возвращающего объект Book
:
@Test
public void givenMockedMethod_whenMethodInvoked_thenReturnBook() {
Long id = 1L;
when(bookRepository.getByBookId(anyLong())).thenAnswer(answer(BookServiceUnitTest::buildBook));
assertNotNull(bookService.getByBookId(id));
assertEquals("The Stranger", bookService.getByBookId(id).getTitle());
}
private static Book buildBook(Long bookId) {
return new Book(bookId, "The Stranger", "Albert Camus", 456);
}
Как показано выше, мы использовали ссылку на метод для обозначения интерфейса Answer1
.
3.2. Использование AdditionalAnswers.answerVoid()
Точно так же мы можем использовать answerVoid()
для настройки ответа макета для вызова аргументов, который ничего не возвращает.
Далее давайте продемонстрируем использование метода AdditionalAnswers.answerVoid()
на тестовом примере:
@Test
public void givenMockedMethod_whenMethodInvoked_thenReturnVoid() {
Long id = 2L;
when(bookRepository.getByBookId(anyLong())).thenAnswer(answerVoid(BookServiceUnitTest::printBookId));
bookService.getByBookId(id);
verify(bookRepository, times(1)).getByBookId(id);
}
private static void printBookId(Long bookId) {
System.out.println(bookId);
}
Как мы видим, мы использовали `` интерфейс ** VoidAnswer1<A0>
** для создания ответа на вызов с одним аргументом, который ничего не возвращает .
Метод ответа
указывает действие, которое выполняется, когда мы взаимодействуем с макетом. В нашем случае мы просто печатаем id переданной книги.
4. Вывод
В целом, в этом руководстве были рассмотрены методы класса AdditionalAnswers
Mockito .
Реализация этих примеров и фрагментов кода доступна на GitHub .