1. Overview
In this tutorial, we'll get familiar with Mockito's AdditionalAnswers class and its methods.
2. Returning Arguments
The main purpose of the AdditionalAnswers class is to return parameters passed to a mocked method.
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.
Furthermore, AdditionalAnswers has different implementations of the Answer class.
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
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:
@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():
@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.
When we run this test, it will indeed return the first argument, which is equal to the Book object we saved.
2.2. Returning the Second Argument
Secondly, we create a test using 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.
2.3. Returning the Last Argument
Similarly, we can use AdditionalAnswers.returnsLastArg() to get the last argument that we passed to our method:
@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
Finally, let's write a test using the method that enables us to return an argument at a given 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.
3. Conclusion
Altogether, this tutorial has covered the methods of Mockito's AdditionalAnswers class.
The implementation of these examples and code snippets are available over on GitHub.