Skip to content

(Argument)Matchers regression from 1.10.19 to 2.18.3 for varargs #1498

@ViToni

Description

@ViToni

While updating from 1.10.19 to 2.18.3 I have a regression regarding Matchers and varargs.
Passing tests now fail because the Matchers don't work anymore in the verify step.

Below is a reduced test case to reprocude the issue. Version 2.22.0 and ArgumentMatcher were also tested with the same (failing) result.

Error:

Argument(s) are different! Wanted:
spyableLogger.debug(
	<any string>,
	<any java.lang.Object[]>
);
-> at tests.MockitoVarargsTest.testLoggerGetsCalled_Matchers(MockitoVarargsTest.java:29)
Actual invocation has different arguments:
spyableLogger.debug(
	"Something happened",
	java.lang.RuntimeException: Oops
);
-> at tests.MockitoVarargsTest.callLoggerDebug(MockitoVarargsTest.java:59)

	at tests.MockitoVarargsTest.testLoggerGetsCalled_Matchers(MockitoVarargsTest.java:29)

Test case:

package tests;

import java.util.Objects;

import org.junit.Test;
//import org.mockito.ArgumentMatchers;
import org.mockito.Matchers;
import org.mockito.Mockito;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;

public class MockitoVarargsTest {
    
    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Test
    public void testLoggerGetsCalled_Matchers() {
        final Logger spyableLogger = new SpyableLogger(logger);
        final Logger loggerSpy = Mockito.spy(spyableLogger);

        final RuntimeException e = new RuntimeException("Oops");

        final String message = "Something happened";
        callLoggerDebug(loggerSpy, message, e);

        // Mockito 1.10.19 works
        // Mockito 2.18.3 fails
        Mockito.verify(loggerSpy, Mockito.times(1)).debug(Matchers.anyString(), Matchers.any(Object[].class));
        Mockito.verify(loggerSpy, Mockito.never()).trace(Matchers.anyString(), Matchers.any(Object[].class));
        Mockito.verify(loggerSpy, Mockito.never()).warn(Matchers.anyString(), Matchers.any(Object[].class));
        Mockito.verify(loggerSpy, Mockito.never()).error(Matchers.anyString(), Matchers.any(Object[].class));

        final String message2 = String.format("%s %s", "Something", "happened");
        Mockito.verify(loggerSpy, Mockito.times(1)).debug(Matchers.eq(message2), Matchers.any(Object[].class));
    }

//    @Test
//    public void testLoggerGetsCalled_ArgumentMatchers() {
//        final Logger spyableLogger = new SpyableLogger(logger);
//        final Logger loggerSpy = Mockito.spy(spyableLogger);
//
//        final RuntimeException e = new RuntimeException("Oops");
//
//        final String message = "Something happened";
//        callLoggerDebug(loggerSpy, message, e);
//
//        // Mockito 2.18.3 fails
//        Mockito.verify(loggerSpy, Mockito.times(1)).debug(ArgumentMatchers.anyString(), ArgumentMatchers.any(Object[].class));
//        Mockito.verify(loggerSpy, Mockito.never()).trace(ArgumentMatchers.anyString(), ArgumentMatchers.any(Object[].class));
//        Mockito.verify(loggerSpy, Mockito.never()).warn(ArgumentMatchers.anyString(), ArgumentMatchers.any(Object[].class));
//        Mockito.verify(loggerSpy, Mockito.never()).error(ArgumentMatchers.anyString(), ArgumentMatchers.any(Object[].class));
//
//        final String message2 = String.format("%s %s", "Something", "happened");
//        Mockito.verify(loggerSpy, Mockito.times(1)).debug(ArgumentMatchers.eq(message2), ArgumentMatchers.any(Object[].class));
//    }

    void callLoggerDebug(final Logger logger, final String message, final Object... args) {
        logger.debug(message, args);
    }

    /**
     * {@link Logger}s returned by {@link LoggerFactory} seem to be final and thus cannot be spied on.
     * We use a delegating non-final class to spy on logging calls.
     */
    private static class SpyableLogger implements Logger {
        final Logger delegate;

        public SpyableLogger(final Logger logger) {
            delegate = Objects.requireNonNull(logger, "'logger' must not be null");
        }

        @Override
        public void debug(Marker arg0, String arg1, Object arg2, Object arg3) {
            delegate.debug(arg0, arg1, arg2, arg3);
        }

        @Override
        public void debug(Marker arg0, String arg1, Object... arg2) {
            delegate.debug(arg0, arg1, arg2);
        }

        @Override
        public void debug(Marker arg0, String arg1, Object arg2) {
            delegate.debug(arg0, arg1, arg2);
        }

        @Override
        public void debug(Marker arg0, String arg1, Throwable arg2) {
            delegate.debug(arg0, arg1, arg2);
        }

        @Override
        public void debug(Marker arg0, String arg1) {
            delegate.debug(arg0, arg1);
        }

        @Override
        public void debug(String arg0, Object arg1, Object arg2) {
            delegate.debug(arg0, arg1, arg2);
        }

        @Override
        public void debug(String arg0, Object... arg1) {
            delegate.debug(arg0, arg1);
        }

        @Override
        public void debug(String arg0, Object arg1) {
            delegate.debug(arg0, arg1);
        }

        @Override
        public void debug(String arg0, Throwable arg1) {
            delegate.debug(arg0, arg1);
        }

        @Override
        public void debug(String arg0) {
            delegate.debug(arg0);
        }

        @Override
        public void error(Marker arg0, String arg1, Object arg2, Object arg3) {
            delegate.error(arg0, arg1, arg2, arg3);
        }

        @Override
        public void error(Marker arg0, String arg1, Object... arg2) {
            delegate.error(arg0, arg1, arg2);
        }

        @Override
        public void error(Marker arg0, String arg1, Object arg2) {
            delegate.error(arg0, arg1, arg2);
        }

        @Override
        public void error(Marker arg0, String arg1, Throwable arg2) {
            delegate.error(arg0, arg1, arg2);
        }

        @Override
        public void error(Marker arg0, String arg1) {
            delegate.error(arg0, arg1);
        }

        @Override
        public void error(String arg0, Object arg1, Object arg2) {
            delegate.error(arg0, arg1, arg2);
        }

        @Override
        public void error(String arg0, Object... arg1) {
            delegate.error(arg0, arg1);
        }

        @Override
        public void error(String arg0, Object arg1) {
            delegate.error(arg0, arg1);
        }

        @Override
        public void error(String arg0, Throwable arg1) {
            delegate.error(arg0, arg1);
        }

        @Override
        public void error(String arg0) {
            delegate.error(arg0);
        }

        @Override
        public String getName() {
            return delegate.getName();
        }

        @Override
        public void info(Marker arg0, String arg1, Object arg2, Object arg3) {
            delegate.info(arg0, arg1, arg2, arg3);
        }

        @Override
        public void info(Marker arg0, String arg1, Object... arg2) {
            delegate.info(arg0, arg1, arg2);
        }

        @Override
        public void info(Marker arg0, String arg1, Object arg2) {
            delegate.info(arg0, arg1, arg2);
        }

        @Override
        public void info(Marker arg0, String arg1, Throwable arg2) {
            delegate.info(arg0, arg1, arg2);
        }

        @Override
        public void info(Marker arg0, String arg1) {
            delegate.info(arg0, arg1);
        }

        @Override
        public void info(String arg0, Object arg1, Object arg2) {
            delegate.info(arg0, arg1, arg2);
        }

        @Override
        public void info(String arg0, Object... arg1) {
            delegate.info(arg0, arg1);
        }

        @Override
        public void info(String arg0, Object arg1) {
            delegate.info(arg0, arg1);
        }

        @Override
        public void info(String arg0, Throwable arg1) {
            delegate.info(arg0, arg1);
        }

        @Override
        public void info(String arg0) {
            delegate.info(arg0);
        }

        @Override
        public boolean isDebugEnabled() {
            return delegate.isDebugEnabled();
        }

        @Override
        public boolean isDebugEnabled(Marker arg0) {
            return delegate.isDebugEnabled(arg0);
        }

        @Override
        public boolean isErrorEnabled() {
            return delegate.isErrorEnabled();
        }

        @Override
        public boolean isErrorEnabled(Marker arg0) {
            return delegate.isErrorEnabled(arg0);
        }

        @Override
        public boolean isInfoEnabled() {
            return delegate.isInfoEnabled();
        }

        @Override
        public boolean isInfoEnabled(Marker arg0) {
            return delegate.isInfoEnabled(arg0);
        }

        @Override
        public boolean isTraceEnabled() {
            return delegate.isTraceEnabled();
        }

        @Override
        public boolean isTraceEnabled(Marker arg0) {
            return delegate.isTraceEnabled(arg0);
        }

        @Override
        public boolean isWarnEnabled() {
            return delegate.isWarnEnabled();
        }

        @Override
        public boolean isWarnEnabled(Marker arg0) {
            return delegate.isWarnEnabled(arg0);
        }

        @Override
        public void trace(Marker arg0, String arg1, Object arg2, Object arg3) {
            delegate.trace(arg0, arg1, arg2, arg3);
        }

        @Override
        public void trace(Marker arg0, String arg1, Object... arg2) {
            delegate.trace(arg0, arg1, arg2);
        }

        @Override
        public void trace(Marker arg0, String arg1, Object arg2) {
            delegate.trace(arg0, arg1, arg2);
        }

        @Override
        public void trace(Marker arg0, String arg1, Throwable arg2) {
            delegate.trace(arg0, arg1, arg2);
        }

        @Override
        public void trace(Marker arg0, String arg1) {
            delegate.trace(arg0, arg1);
        }

        @Override
        public void trace(String arg0, Object arg1, Object arg2) {
            delegate.trace(arg0, arg1, arg2);
        }

        @Override
        public void trace(String arg0, Object... arg1) {
            delegate.trace(arg0, arg1);
        }

        @Override
        public void trace(String arg0, Object arg1) {
            delegate.trace(arg0, arg1);
        }

        @Override
        public void trace(String arg0, Throwable arg1) {
            delegate.trace(arg0, arg1);
        }

        @Override
        public void trace(String arg0) {
            delegate.trace(arg0);
        }

        @Override
        public void warn(Marker arg0, String arg1, Object arg2, Object arg3) {
            delegate.warn(arg0, arg1, arg2, arg3);
        }

        @Override
        public void warn(Marker arg0, String arg1, Object... arg2) {
            delegate.warn(arg0, arg1, arg2);
        }

        @Override
        public void warn(Marker arg0, String arg1, Object arg2) {
            delegate.warn(arg0, arg1, arg2);
        }

        @Override
        public void warn(Marker arg0, String arg1, Throwable arg2) {
            delegate.warn(arg0, arg1, arg2);
        }

        @Override
        public void warn(Marker arg0, String arg1) {
            delegate.warn(arg0, arg1);
        }

        @Override
        public void warn(String arg0, Object arg1, Object arg2) {
            delegate.warn(arg0, arg1, arg2);
        }

        @Override
        public void warn(String arg0, Object... arg1) {
            delegate.warn(arg0, arg1);
        }

        @Override
        public void warn(String arg0, Object arg1) {
            delegate.warn(arg0, arg1);
        }

        @Override
        public void warn(String arg0, Throwable arg1) {
            delegate.warn(arg0, arg1);
        }

        @Override
        public void warn(String arg0) {
            delegate.warn(arg0);
        }

    }

}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions