Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Mockito Varargs parameter when thenAnswer

I don’t understand why a Class Cast exception is thrown here:

I have a method that takes as parameter a varargs of String

public List<A> getSomething(final String a, final String b, final Date c, final String d, final String e, final String... f) throws MyException {

   return valuesFromDB(a, b, c, d, e, f);

}

I have mocked the method implementation

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

public List<A> getSomethingUnitTest(final String a, final String b, final Date c, final String d, final String e, final String... f) throws MyException {

   return valuesFromCSV(a, b, c, d, e, f);

}

on Unit Test using Mockito

@ExtendWith(MockitoExtension.class)
class AlgoTest {

    @Test
    void testExecute() throws MyException {
    
        when(myRealService.getSomething(anyString(), anyString(), any(Date.class), anyString(), anyString(), any()))
            .thenAnswer(i -> myMockedService.getSomething(i.getArgument(0), i.getArgument(1), i.getArgument(2), i.getArgument(3), i.getArgument(4), i.getArgument(5)));

        Output output = algo.execute();
        
        assertNotNull(output);
   
    }

}

when I run the test a Class Cast Exception is thrown

java.lang.ClassCastException: class java.lang.String cannot be cast to class [Ljava.lang.String; (java.lang.String and [Ljava.lang.String; are in module java.base of loader 'bootstrap')
at com.cmystuff.alghorithm.AlgoTest.lambda$2(AlgoTest.java:82)
at org.mockito.internal.stubbing.StubbedInvocationMatcher.answer(StubbedInvocationMatcher.java:40)
at org.mockito.internal.handler.MockHandlerImpl.handle(MockHandlerImpl.java:99)
at org.mockito.internal.handler.NullResultGuardian.handle(NullResultGuardian.java:29)
at org.mockito.internal.handler.InvocationNotifierHandler.handle(InvocationNotifierHandler.java:33)
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.interceptSuperCallable(MockMethodInterceptor.java:141)

any doucumentation:

<String[]> String[] org.mockito.ArgumentMatchers.any()
Matches anything, including nulls and varargs. 

getArgument Documentation:

<String[]> String[] org.mockito.invocation.InvocationOnMock.getArgument(int index)

Returns casted argument at the given index. Can lookup in expanded arguments form getArguments().
This method is preferred over getArgument(int, Class) for readability. 
Please readthe documentation of getArgument(int, Class) for an overview of situations whenthat method is preferred over this one.

I also tried to use anyString() but I got the same Class Cast Exception

@ExtendWith(MockitoExtension.class)
class AlgoTest {

    @Test
    void testExecute() throws MyException {
    
        when(myRealService.getSomething(anyString(), anyString(), any(Date.class), anyString(), anyString(), anyString()))
            .thenAnswer(i -> myMockedService.getSomething(i.getArgument(0), i.getArgument(1), i.getArgument(2), i.getArgument(3), i.getArgument(4), i.getArgument(5)));

        Output output = algo.execute();
        
        assertNotNull(output);
   
    }

}

The issue seems to be related to varargs with one value…
if I pass two values for the varargs parameter

@ExtendWith(MockitoExtension.class)
class AlgoTest {

    @Test
    void testExecute() throws MyException {
    
        when(myRealService.getSomething(anyString(), anyString(), any(Date.class), anyString(), anyString(), anyString(), anyString()))
            .thenAnswer(i -> myMockedService.getSomething(i.getArgument(0), i.getArgument(1), i.getArgument(2), i.getArgument(3), i.getArgument(4), i.getArgument(5), i.getArgument(6)));

        Output output = algo.execute();
        
        assertNotNull(output);
   
    }

}

this one will work fine.

>Solution :

Method taking varargs is represented in runtime as a method taking an array as its last argument. The fact that you can pass arguments as separate values in your source code is only syntax sugar.

The problem with your code is that i.getArgument(index) returns each separate value – it does not group varargs into an array.

For example, it you call:

myRealService.getSomething("0", "1", new Date(), "3", "4", "5", "6", "7");
  • i.getArgument(5) is equal to "5"
  • you pass this value to the varargs method, while you should be passing an array instead
  • you can easily copy the values corresponding to original varargs to a new array (of type String[])
when(myRealService.getSomething(
        anyString(),
        anyString(),
        any(Date.class),
        anyString(),
        anyString(),
        any())
).thenAnswer(i -> {
    String[] varargFromInvocation = Arrays.copyOfRange(i.getArguments(), 5, i.getArguments().length, String[].class);
    return myMockedService.getSomething(
            i.getArgument(0),
            i.getArgument(1),
            i.getArgument(2),
            i.getArgument(3),
            i.getArgument(4),
            varargFromInvocation);
});
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading