Skip to content

Varargs methods cause ClassCastException in AnswerFunctionalInterfaces #2644

@perlun

Description

@perlun

Hi,

The other day, I discovered that this code does not work:

        doAnswer(answerVoid((VoidAnswer2<String, Object[]>)
                logger::info
        )).when(mock)
                .info(any(), (Object[]) any());

        // This call will throw an exception
        mock.info("Some message with {} {} {}", "three", "parameters", "");

The exception details looks as follows:

class java.lang.String cannot be cast to class [Ljava.lang.Object; (java.lang.String and [Ljava.lang.Object; are in module java.base of loader 'bootstrap')
java.lang.ClassCastException: class java.lang.String cannot be cast to class [Ljava.lang.Object; (java.lang.String and [Ljava.lang.Object; are in module java.base of loader 'bootstrap')
	at org.mockito.internal.stubbing.answers.AnswerFunctionalInterfaces$4.answer(AnswerFunctionalInterfaces.java:97)
	at org.mockito.internal.stubbing.answers.AnswerFunctionalInterfaces$4.answer(AnswerFunctionalInterfaces.java:93)
	at org.mockito.internal.stubbing.StubbedInvocationMatcher.answer(StubbedInvocationMatcher.java:42)
	at org.mockito.internal.handler.MockHandlerImpl.handle(MockHandlerImpl.java:103)
	at org.mockito.internal.handler.NullResultGuardian.handle(NullResultGuardian.java:29)
	at org.mockito.internal.handler.InvocationNotifierHandler.handle(InvocationNotifierHandler.java:34)
	at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor.doIntercept(MockMethodInterceptor.java:82)
	at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor.doIntercept(MockMethodInterceptor.java:56)
	at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor$DispatcherDefaultingToRealMethod.interceptAbstract(MockMethodInterceptor.java:161)
	at org.slf4j.Logger$MockitoMock$f4Org7xs.info(Unknown Source)
	at LoggerMockingTest.logger_info_can_be_called_without_exceptions(LoggerMockingTest.java:34)
        [...]

The following works, but feels much less elegant:

        doAnswer( invocation -> {
            String format = invocation.getArgument( 0 );
            Object[] allArguments = invocation.getArguments();
            Object[] arguments = Arrays.copyOfRange( allArguments, 1, allArguments.length );
            logger.info( format, arguments );
            return null;
        } ).when( mock )
                .info( anyString(), (Object[])any() );

Is this to be considered a bug or a feature? 🙂 I get the feeling that varargs (Object[] under the hood) parameters do not really work when used with the answerVoid()-based approach as described above.

Full MCVE

https://github.com/perlun/mockito-slf4-logging-example

Issue checks

  • The mockito message in the stacktrace have useful information, but it didn't help
  • The problematic code (if that's possible) is copied here;
    Note that some configuration are impossible to mock via Mockito
  • Provide versions (mockito / jdk / os / any other relevant information): Reproduced with latest Mockito, 4.5.1
  • Provide a Short, Self Contained, Correct (Compilable), Example of the issue
  • Read the contributing guide

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