Search code examples
javajenkinsgroovymockitomatcher

Mockito incorrect argument matcher after Jenkins core upgrade


We have Jenkins shared library project with some unit-tests that utilize Mockito. After an upgrade of Jenkins-core from version 2.325 to 2.326 tests start failing on the following line:

class DSLMock {

  DSLMock() {

    this.mock = mock(DSL.class)

->  when(mock.invokeMethod(eq("error"), any())).then(new Answer<String>() {
      @Override
      String answer(InvocationOnMock invocationOnMock) throws Throwable {
        throw new AbortException((String) invocationOnMock.getArguments()[1][0])
      }
    })
...

with error:


org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 
Misplaced or misused argument matcher detected here:
-> at com.devops.jenkins.testing.DSLMock.<init>(DSLMock.groovy:66)
-> at com.devops.jenkins.testing.DSLMock.<init>(DSLMock.groovy:66)
You cannot use argument matchers outside of verification or stubbing.
Examples of correct usage of argument matchers:
    when(mock.get(anyInt())).thenReturn(null);
    doThrow(new RuntimeException()).when(mock).someVoidMethod(anyObject());
    verify(mock).someMethod(contains("foo"))
This message may appear after an NullPointerException if the last matcher is returning an object 
like any() but the stubbed method signature expect a primitive argument, in this case,
use primitive alternatives.
    when(mock.get(any())); // bad use, will raise NPE
    when(mock.get(anyInt())); // correct usage use
Also, this error might show up because you use argument matchers with methods that cannot be mocked.
Following methods *cannot* be stubbed/verified: final/private/equals()/hashCode().
Mocking methods declared on non-public parent classes is not supported.

I've tried to replace any() with methods like anyString() and just value like "" but still got same error. Also I've tried different stub syntax like

doAnswer(new Answer...).when(mock).invokeMethod(eq("error"), any())

In changelog https://www.jenkins.io/changelog-old/#v2.326 I see Groovy patch version has been upgraded:

  • Upgrade Groovy from 2.4.12 to 2.4.21

I wonder if that would cause the issue. Other dependencies versions are not changed:

<groovy.version>2.4.12</groovy.version>
<junit-jupiter.version>5.8.1</junit-jupiter.version>
<mockito.core.version>3.3.3</mockito.core.version>

Solution

  • It seems like there have been similar issues with other versions of Groovy and Mockito: Is Groovy 3.0.5 incompatible with Mockito 3.6.28 ? Mocks are not usable

    However, even the latest version of Mockito doesn't seem to fix the issue with the version of Groovy that you're using. In the meantime, this worked for me:

    @Test
    void shouldMockObject() {
        DSL mock = spy(new DSL(null) {
            @Override
            Object invokeMethod(String name, Object args) {
                return null
            }
        })
    
        when(mock.invokeMethod(eq("error"), any())).then(new Answer<String>() {
            @Override
            String answer(InvocationOnMock invocationOnMock) throws Throwable {
                throw new AbortException((String) invocationOnMock.getArguments()[1][0])
            }
        })
    
        assertThrows(AbortException.class) {
            mock.invokeMethod("error", [ "message" ])
        }
    }