Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting InvalidUseOfMatchersException from call to verify() even though all arguements are of type 'Matchers'

Tags:

java

mockito

I have the following code in a test which uses the Mockito framework to verify that the method drawTextOnCanvas() was called with the correct parameters.

    // The two objects below are mocks.  The rest of the objects used in
    // the call to verify() are plain old java objects I've instantiated elsewhere.
    BufferedImage testImage = Mockito.mock(BufferedImage.class);
    Mockito.when(testImage.getHeight()).thenReturn(10);

    Graphics mockGraphics = Mockito.mock(Graphics.class);
    Mockito.when(mockGraphics.getFontMetrics(Matchers.any(Font.class)))
            .thenReturn(Mockito.mock(FontMetrics.class));


    Mockito.verify(drawingUtil).drawTextOnCanvas(
            Matchers.eq(imageCategory.getWidth()), 
            Matchers.eq(mockGraphics),
            Matchers.any(Font.class), 
            Matchers.eq(Arrays.asList("Test text")),
            Matchers.eq(testImage.getHeight() + 10),
            Matchers.any(FontMetrics.class), 
            Matchers.eq(10));

However it throws the following exception:

org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 
Invalid use of argument matchers!
0 matchers expected, 4 recorded:
-> at com.test.package(ExampleTest.java:66)
-> at com.test.package(ExampleTest.java:67)
-> at com.test.package(ExampleTest.java:67)
-> at com.test.package(ExampleTest.java:68)

This exception may occur if matchers are combined with raw values:
    //incorrect:
    someMethod(anyObject(), "raw String");
When using matchers, all arguments have to be provided by matchers.
For example:
    //correct:
    someMethod(anyObject(), eq("String by matcher"));

For more info see javadoc for Matchers class........

I have however discovered that if I replace the line

 Matchers.eq(testImage.getHeight() + 10), 

with

 Matchers.eq(someInteger),

the test runs with no exceptions, which is what puzzles me.

I've looked at the JavaDoc for Matchers and as far as I can see what I've written should work, unless there is some rule about putting calls to mock objects inside a call to eq() that I've missed.

like image 449
Matt Watson Avatar asked Oct 20 '22 05:10

Matt Watson


1 Answers

You've nailed it: You've mocked testImage, and calling a mock method while stubbing is not allowed. The documentation is not clear on that limitation.

I wrote an exhaustive answer on Mockito matchers before (see "implementation details"), but the short version is that Mockito keeps its argument matchers on a stack and that every call to a mocked n-argument method checks that the stack contains exactly 0 or n matchers.

Mockito.verify(drawingUtil).drawTextOnCanvas(
        Matchers.eq(imageCategory.getWidth()),      // A1
        Matchers.eq(mockGraphics),                  // A2
        Matchers.any(Font.class),                   // A3
        Matchers.eq(Arrays.asList("Test text")),    // A4
        Matchers.eq(testImage.getHeight() + 10),    // B
        Matchers.any(FontMetrics.class),            // C1
        Matchers.eq(10));                           // C2

The call order is something like this: After verify(drawingUtil), Java prepares the arguments to drawTextOnCanvas in order, calling A1 through A4. Suddenly the call to B happens, and Mockito prepares as if textImage.getHeight() were embedded within a when(...) call. There are 4 matchers on the stack, and Mockito expects either to match by equality (zero matchers) or the number of arguments to getHeight() (also zero matchers). Mockito throws that exception with that "4 matchers recorded" message before even getting to C1, C2, or drawTextOnCanvas.

As a matter of good practice, extract all Matchers.eq values (that contain method calls) to local variables, with a particular emphasis on calls to mocked objects.

like image 171
Jeff Bowman Avatar answered Oct 21 '22 22:10

Jeff Bowman