Издевательский метод, возвращающий generics с подстановочным знаком, используя mockito

Я использую mockito 1.9.5. У меня есть следующий код:

public class ClassA  {

public List<? extends MyInterface> getMyInterfaces() {
    return null;
}

public static void testMock() {
    List<MyInterface> interfaces = new ArrayList<>();
    ClassA classAMock = mock(ClassA.class);
    when(classAMock.getMyInterfaces()).thenReturn(interfaces);      
}

Я получаю ошибку компиляции для thenReturn(interfaces), говорящего:

"The method thenReturn(List<capture#1-of ? extends MyInterface>) in the type 
 OngoingStubbing<List<capture#1-of ? extends MyInterface>> is not applicable for the arguments 
 (List<MyInterface>)"

Однако, когда я использую метод thenAnswer mockito, я не получаю ошибку. Может ли кто-нибудь сказать мне, что происходит? Почему я получаю сообщение об ошибке при использовании метода thenReturn? Есть ли другой способ решить эту проблему, если ClassA предоставляется третьей стороной и не может быть изменен?

Ответ 1

EDIT. Начиная с Mockito 1.10.x, типы дженериков, встроенные в класс, теперь используются Mockito для глубоких заглушек. то есть.

public interface A<T extends Observer & Comparable<? super T>>  {
  List<? extends B> bList();
  T observer();
}

B b = deep_stubbed.bList().iterator().next(); // returns a mock of B ; mockito remebers that A returns a List of B
Observer o = deep_stubbed.observer(); // mockito can find that T super type is Observer
Comparable<? super T> c = deep_stubbed.observer(); // or that T implements Comparable

Mockito изо всех сил пытается получить информацию о типе, которую использует компилятор, но когда применяется стирание, mockito не может ничего сделать, кроме как вернуть mock из Object.


Оригинал: Ну, это больше проблема с дженериками, чем с Mockito. Для дженериков вы должны прочитать, что на них написала Angelika Langer. И для текущей темы, то есть подстановочных знаков, прочтите это section.

Но для краткости, то, что вы могли бы использовать, это другой синтаксис Mockito, который поможет вам в вашей текущей ситуации:

doReturn(interfaces).when(classAMock).getMyInterfaces();

Или с помощью псевдонимов BDD:

willReturn(interfaces).given(classAMock).getMyInterfaces();

Тем не менее, вы можете написать обертки, которые более универсальны. Это поможет будущим разработчикам работать с одним и тем же сторонним API.


В качестве побочного примечания: вы не должны издеваться над типом, которым вы не владеете, это может привести ко многим ошибкам и проблемам. Вместо этого у вас должна быть оболочка. DAO и репозитории, например, представляют такую ​​идею, они будут издеваться над интерфейсом DAO или репозитория, но не с JDBC/JPA/hibernate. Об этом много сообщений в блоге:

Ответ 2

Другим решением (хотя и менее удобочитаемым) является определение статического метода вызова when для привязки шаблона:

Mockito.<List<? extends MyInterface>>when(classAMock.getMyInterfaces()).thenReturn(interfaces);