1. Overview
1.概述
In this tutorial, we’ll get familiar with Mockito’s AdditionalAnswers class and its methods.
在本教程中,我们将熟悉Mockito的AdditionalAnswers类和其方法。
2. Returning Arguments
2.返回论据
The main purpose of the AdditionalAnswers class is to return parameters passed to a mocked method.
AdditionalAnswers类的主要目的是为了返回传递给模拟方法的参数。
For example, when updating an object, the method being mocked usually just returns the updated object. Using the methods from AdditionalAnswers, we can instead return a specific parameter passed as an argument to the method, based on its position in the parameter list.
例如,当更新一个对象时,被模拟的方法通常只返回更新的对象。使用AdditionalAnswers中的方法,我们可以根据参数列表中的位置,返回作为参数传递给该方法的特定参数。
Furthermore, AdditionalAnswers has different implementations of the Answer class.
此外,AdditionalAnswers对Answer类有不同的实现。
To begin our demonstration, let’s create a library project.
为了开始我们的演示,让我们创建一个库项目。
Firstly, we’ll create one simple model:
首先,我们将创建一个简单的模型。
public class Book {
private Long bookId;
private String title;
private String author;
private int numberOfPages;
// constructors, getters and setters
}
Additionally, we need a repository class for book retrieval:
此外,我们需要一个用于图书检索的存储库类。
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()));
}
}
Correspondingly, we have a service class that invokes our repository methods:
相应地,我们有一个服务类来调用我们的存储库方法。
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);
}
}
With this in mind, let’s create some tests.
考虑到这一点,让我们创建一些测试。
2.1. Returning the First Argument
2.1.返回第一个参数
For our test class, we need to enable the use of annotations with Mockito tests by annotating the JUnit test class to run with MockitoJUnitRunner. Furthermore, we’ll need to mock our service and repository class:
对于我们的测试类,我们需要通过注释JUnit测试类来启用注解与Mockito测试的使用,以便与MockitoJUnitRunner运行。此外,我们还需要模拟我们的服务和存储库类。
@RunWith(MockitoJUnitRunner.class)
public class BookServiceUnitTest {
@InjectMocks
private BookService bookService;
@Mock
private BookRepository bookRepository;
// test methods
}
Firstly, lets create a test returning the first argument – AdditionalAnswers.returnsFirstArg():
首先,让我们创建一个返回第一个参数的测试 – 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);
}
In other words, we’ll mock the save method from our BookRepository class, which accepts the Book object.
换句话说,我们将从我们的BookRepository类中模拟save方法,它接受Book对象。
When we run this test, it will indeed return the first argument, which is equal to the Book object we saved.
当我们运行这个测试时,它确实会返回第一个参数,这等于我们保存的Book对象。
2.2. Returning the Second Argument
2.2.返回第二个论据
Secondly, we create a test using AdditionalAnswers.returnsSecondArg():
其次,我们使用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);
}
In this case, when our selectRandomBook method executes, the method will return the second book.
在这种情况下,当我们的selectRandomBook方法执行时,该方法将返回第二本书。
2.3. Returning the Last Argument
2.3.返回最后一个参数
Similarly, we can use AdditionalAnswers.returnsLastArg() to get the last argument that we passed to our method:
同样地,我们可以使用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);
}
Here, the method invoked will return the third book, as it is the last parameter.
这里,调用的方法将返回第三本书,因为它是最后一个参数。
2.4. Returning the Argument at Index
2.4.返回索引处的论据
Finally, let’s write a test using the method that enables us to return an argument at a given index – AdditionalAnswers.returnsArgAt(int index):
最后,让我们用使我们能够在给定的索引处返回一个参数的方法写一个测试 – 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);
}
In the end, since we asked for the argument from index 1, we’ll get the second argument — specifically, book2 in this case.
最后,由于我们要求的是索引1的参数,我们将得到第二个参数–具体地说,在本例中是book2。
3. Creating an Answer From a Functional Interface
3.从功能界面创建一个答案
AdditionalAnswers offers a concise and neater way to create answers from functional interfaces. To do so, it provides two convenient methods: answer() and answerVoid().
AdditionalAnswers提供了一种简洁明了的方式来从functional interfaces创建答案。为了做到这一点,它提供了两个方便的方法。answer()和answerVoid()。
So, let’s go down the rabbit hole and see how to use them in practice.
因此,让我们走进兔子洞,看看如何在实践中使用它们。
Please bear in mind that these two methods are annotated with @Incubating. This means that they might change at a later time based on the community feedback.
请记住,这两个方法被注释为@Incubating。这意味着它们可能在以后的时间里根据社区的反馈而改变。
3.1. Using AdditionalAnswers.answer()
3.1.使用AdditionalAnswers.answer()
This method is mainly introduced to create a strongly typed answer in Java 8 using a functional interface.
这种方法主要是为了在Java 8中使用函数式接口创建一个强类型的答案而引入的。
Typically, Mockito comes with a set of ready-to-use generic interfaces that we can use to configure a mock’s answer. For example, it provides Answer1<T,A0> for a single argument invocation.
通常情况下,Mockito带有一套现成的通用接口,我们可以用它来配置一个模拟的答案。例如,它为单参数调用提供了Answer1<T,A0>。
Now, let’s illustrate how to use the AdditionalAnswers.answer() to create an answer that returns a Book object:
现在,让我们来说明如何使用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);
}
As shown above, we used a method reference to denote the Answer1 interface.
如上所示,我们使用方法引用来表示Answer1接口。
3.2. Using AdditionalAnswers.answerVoid()
3.2.使用AdditionalAnswers.answerVoid()
Similarly, we can use answerVoid() to configure a mock’s answer for arguments invocation that returns nothing.
类似地,我们可以使用answerVoid()来配置mock对不返回任何参数的调用的回答。
Next, let’s exemplify the use of the AdditionalAnswers.answerVoid() method using a test case:
接下来,让我们用一个测试案例举例说明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);
}
As we can see, we used the VoidAnswer1<A0> interface to create an answer for a single argument invocation that returns nothing.
我们可以看到,我们使用了VoidAnswer1<A0>接口为一个单参数调用创建一个答案,不返回。
The answer method specifies an action that is executed when we interact with the mock. In our case, we simply print the passed book’s id.
answer方法指定了一个动作,当我们与mock互动时,这个动作将被执行。在我们的例子中,我们只是简单地打印了传递的书的ID。
4. Conclusion
4.总结
Altogether, this tutorial has covered the methods of Mockito’s AdditionalAnswers class.
总的来说,本教程涵盖了Mockito的AdditionalAnswers类的方法。
The implementation of these examples and code snippets are available over on GitHub.
这些例子的实现和代码片段可在GitHub上获得over 。