2

I am writing unit test case for update method for book.

@Test
void testUpdateBook() {
    //initializing the bookId here
    givenBookId();
    //initializing the book here 
    givenBook();
    givenMock();
    //call the updateBook method 
    whenUpdateBookRequested();
    thenVerifyBook();
}

givenMock() {
   when(bookRepository.updateBook(bookId,any()).thenReturn(Optional.ofNullable(expectedBook));
}

Every other method is working fine but givenMock() is producing error:

Invalid use of argument matchers. 2 matchers expected, 1 recorded

And if I write the same method like below, then test is running successfully but I am not able to understand the meaning of eq(). Can someone please explain the difference between the previous and the modified method?

givenMock(){
    when(bookRepository.updateBook(eq(bookId),any()).thenReturn(Optional.ofNullable(expectedBook));
}
ETO
  • 6,970
  • 1
  • 20
  • 37
  • You're telling the mock that when it receives a parameter that equals `bookId` then... etc... – Federico klez Culloca Jul 25 '22 at 08:36
  • 1
    When you use Mockito you're stubbing a real method call. If you say `when(bookRepository.updateBook(...)`, then Mockito will build an ongoing stubbing of your `bookRepository` and each time it is called for real, it will intercept the call dynamically and do what you tell it to do. So since you're not calling the real method but a proxified one, you need to pass the objects that Mockito expects. Those objects are called matchers, and `eq()` creates a matcher that says true only when the object is exactly equal to that one. Though, you can't use the real object directly. – Matteo NNZ Jul 25 '22 at 09:30

2 Answers2

2

This is directly related to the way Mockito is implemented.

When you stub a method Mockito expects you to pass the argument values or matchers. There's a very important detail you should take into account. The arguments should be either all exact values or all matchers.

Look at these examples:

when(repository.updateBook(bookId, any()).thenReturn(something);       // error
when(repository.updateBook(any(), someBook).thenReturn(something);     // error

when(repository.updateBook(bookId, someBook).thenReturn(something);    // valid
when(repository.updateBook(eq(bookId), any()).thenReturn(something);   // valid
when(repository.updateBook(any(), eq(someBook)).thenReturn(something); // valid
when(repository.updateBook(any(), any()).thenReturn(something);        // valid

The error message tells you exactly this. Mockito expected both arguments to be matchers, but instead it found only one.

Invalid use of argument matchers. 2 matchers expected, 1 recorded
ETO
  • 6,970
  • 1
  • 20
  • 37
1

As ETO described, and as the Mockito documentation describes:

Warning:
If you are using argument matchers, all arguments have to be provided by matchers.

I have a deeper write-up in the question How do Mockito matchers work?, but in short, calls to Matchers work via side effects. When you call any() or eq(bookId), Mockito returns a dummy value like null or 0 and saves the Matcher details ("any" or "equals zero") onto its stack. Consequently, when Mockito sees the call to when, there should be exactly 0 matchers (use equality) or 2 matchers (use matchers) on the stack.

For a call like when(yourMock).method(0, anyInt()).thenVerb(...), Mockito can't tell which of the two integer parameters is supposed to be exactly zero and which of the parameters is supposed to be any value, so it throws the error you see. eq is the default behavior if you don't use matchers, so you may rarely see eq unless you're being extra-explicit or if you are mixing eq-style matching with other matchers like any.

Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251