Mockito - Mocking перегруженных методов с параметрами Varargs - PullRequest
1 голос
/ 27 марта 2019

Mockito сложно использовать, когда нам нужно смоделировать перегруженные методы, когда один из них использует varargs.Рассмотрим приведенные ниже методы из Spring * RestTemplate

void put(String url, Object request, Object... uriVariables) throws RestClientException;

void put(String url, Object request, Map<String, ?> uriVariables) throws RestClientException;

Насмешка над вторым - прямая, но насмешка над первым невозможна, так как использование any() приведет к неоднозначному вызову метода, совпадающему с обоими методамии нет никакой альтернативы, чтобы соответствовать просто Object...

Обмен решением в виде вопросов и ответов, который я прибыл после того, как приложил некоторые усилия, чтобы помочь тем, кто находится в одной лодке.Все другие альтернативы приветствуются.

1 Ответ

1 голос
/ 27 марта 2019

Решение этой проблемы может быть предпринято путем использования этой функции для предоставления defaultAnswer макету.DefaultAnswer оценит, что вызов предназначен для конкретного метода, и выполнит необходимое действие, и позволит вызову следовать естественному потоку, если требуемый метод не нацелен.

Это можно хорошо объяснить на примере.Рассмотрим два перегруженных метода из приведенного ниже класса:

public class StringConcat {
    public String concatenate(int i, String... strings) {
        return i + Arrays.stream(strings).collect(Collectors.joining(","));
    }

    public String concatenate(int i, List<String> strings) {
        return i + strings.stream().collect(Collectors.joining(","));
    }
}

Второй метод может быть смоделирован с использованием Mockito, как показано ниже:

StringConcat stringConcat = mock(StringConcat.class);
when(stringConcat.concatenate(anyInt(), anyList())).thenReturn("hardcoded value");

Для представления varargs у нас нет anyVararg()метод (устарел и не работает, не уверен, работал ли он в более старых версиях).Но то же самое можно сделать, создав макет с defaultAnswer, как показано ниже:

@Test
void testWithDefaultAnswer(){
    // Creating mock object with default answer
    StringConcat stringConcat = mock(StringConcat.class, invocation -> {
        Method method = invocation.getMethod();
        if (method.getName().contains("concatenate") && 
               method.getParameters()[method.getParameters().length-1].isVarArgs()){
            if(invocation.getArguments().length>=method.getParameterCount()){
                List varArgParams = Arrays.stream(invocation.getArguments())
                          .skip(method.getParameterCount()-1)
                          .collect(Collectors.toList());
                return invocation.getArguments()[0]+":"
                      +varArgParams.toString(); // mocked result when varargs provided
            }
            return ""+invocation.getArguments()[0]; // mocked result when varargs not provided
        }
        return Answers.RETURNS_DEFAULTS.answer(invocation); // Ensures seamless mocking of any other methods
    });

    // Mock any non varargs methods as needed
    when(stringConcat.concatenate(anyInt(), anyList())).thenReturn("hardcoded"); // mocking as usual

    // Test the mocks
    System.out.println(stringConcat.concatenate(1, "a", "b")); // default answer based mock, with varargs provided
    System.out.println(stringConcat.concatenate(1)); // default answer based mock, without varargs provided
    System.out.println(stringConcat.concatenate(1, Lists.newArrayList("a", "b"))); // mocked non varargs method
}

Вывод:

1:[a, b]
1
hardcoded
...