2

See this example:

class Foo { }

class Bar {
    void takeIt(int i, String arg) { System.out.println(arg + i); }
}

public class Mcve {    
    @Test
    public void passes() {
        Foo foo = Mockito.mock(Foo.class);
        Bar bar = Mockito.mock(Bar.class);
        bar.takeIt(42, "surprise: " + foo);
        String substring = "surprise: " + foo;
        Mockito.verify(bar).takeIt(ArgumentMatchers.eq(42),
                ArgumentMatchers.contains(substring));
    }

    @Test
    public void fails() {
        Foo foo = Mockito.mock(Foo.class);
        Bar bar = Mockito.mock(Bar.class);
        bar.takeIt(42, "surprise: " + foo);
        Mockito.verify(bar).takeIt(ArgumentMatchers.eq(42),
                ArgumentMatchers.contains("surprise: " + foo));
    }    
}

The two tests are almost identical, the only difference: the string used for the contains() matcher is computed upfront in passes(), but inlined in fails().

fails() throws up:

org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 
Invalid use of argument matchers!
0 matchers expected, 1 recorded:
-> at com.ibm.hwmca.z.svm.zhyp.managed.Mcve.fails(Mcve.java:43)

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.


    at java.lang.String.valueOf(String.java:2994)
    at java.lang.StringBuilder.append(StringBuilder.java:131)
    at Mcve.fails(Mcve.java:43)
...

(Obviously: the error message is plain wrong, as the above code is using a matcher for all parameters)

Even more interesting: it only fails with more than 1 argument to match on (if one removes the int parameter from gimme(), and just passes/matches that string argument: pass).

Can anyone explain exactly what is happening here, and is there a way to do such a matching like contains("surprise: " + foo), with foo being something Mockito-mocked?


Of course, this is really meant as MCVE. It took me 3 hours to get from the failing unit test in our environment to this example here.

In the real environment, the Bar class is a mocked logging facility. And the Foo object represents some "data entity" that gets created by some fake persistence layer. I have to verify that the production code does log specific information, and some of that information is derived from the faked data objects.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • 1
    Guessing this is a dumbed down example. It might help to give it some context. I wouldn't expect matchers to work well using string concatenation with an object that doesn't implement `toString()`. I also wouldn't expect mocks to be passed as a param, but could imagine use cases. – DCTID Aug 21 '20 at 02:07
  • @DCTID You want to write a short unit test and print `someMockitoMock.toString()`... of course that results in something meaningful. And yes, my code is very much dumbed down, that is the whole point of a MCVE. But I took your advice, and updated my question on that part. – GhostCat Aug 21 '20 at 06:05
  • My guess is that this problem has to do with the order of the program. In `passes` you create `substring`, create the `eq` matcher, and then create the `contains` matcher. In `fails` you create the `eq` matcher, create the "`substring`", and then create the `contains` matcher. The matchers are "reported" to Mockito's internal mock-progress tracking, as you can see if you look at the source code. It's possible calling `toString()` on a mock, and thus interacting with a mock, _between_ creating two matchers is the root of the issue (bug or not). – Slaw Aug 21 '20 at 06:38
  • But to add a disclaimer: I've never contributed to Mockito nor studied the source code in any significant manner, so I could be completely off with my guess. – Slaw Aug 21 '20 at 06:40
  • @Slaw I guess it is something along those lines. I wrote an issue on their github tracker ... let's see what comes out of that. – GhostCat Aug 21 '20 at 08:25
  • Any resolution? – Slaw Sep 07 '20 at 04:49
  • No solution, and unless I missed to enable notifications, no reaction to my defect. – GhostCat Sep 07 '20 at 04:52

0 Answers0