Mockito isA (Class<T> clazz) Как разрешить безопасность типов?
В моем тесте у меня есть следующая строка:
when(client.runTask(anyString(), anyString(), isA(Iterable.class)).thenReturn(...)
isA(Iterable.class)
выдает предупреждение о том, что необходимо преобразование без проверки для соответствия Iterable<Integer>
, Каков синтаксис для этого?
isA(Iterable<Integer>.class)
isA((Iterable<Integer>)Iterable.class
не работай.
Какие-либо предложения?
4 ответа
Mockito/Hamcrest и общие классы
Да, это общая проблема с Mockito/Hamcrest. Вообще используя isA()
с родовыми классами выдает предупреждение.
Для самых распространенных универсальных классов есть предопределенные сопоставления Mockito: anyList (), anyMap()
, anySet()
а также anyCollection()
,
Предложения:
anyIterable() в Mockito 2.1.0
В Mockito 2.1.0 добавлен новый метод anyIterable() для сопоставления Iterables:
when(client.runTask(anyString(), anyString(), anyIterable()).thenReturn(...)
Игнорировать в Eclipse
Если вы просто хотите избавиться от предупреждения в Eclipse. Опция существует с Eclipse Indigo:
Окно> Настройки> Java > Компилятор> Ошибки / Предупреждения> Универсальные типы> Игнорировать неизбежные проблемы универсальных типов
Быстрое исправление с помощью @SuppressWarnings
Я предлагаю вам сделать это, если у вас есть проблема только один раз. Я лично не помню, чтобы когда-либо нуждался в isA(Iterable.class)
,
Как говорит Даниэль Приден, вы можете ограничить @SuppressWarnings
к локальной переменной или вспомогательному методу.
Используйте универсальное сопоставление isA() с TypeToken
Это решает проблему навсегда. Но у него есть два недостатка:
- Синтаксис не слишком красивый и может запутать некоторых людей.
- У вас есть дополнительная зависимость от библиотеки, предоставляющей
TypeToken
учебный класс. Здесь я использовал класс TypeToken из Гуавы. Там также естьTypeToken
класс в Gson иGenericType
в JAX-RS.
Использование универсального сопоставителя:
import static com.arendvr.matchers.InstanceOfGeneric.isA;
import static org.mockito.ArgumentMatchers.argThat;
// ...
when(client.runTask(anyString(), anyString(), argThat(isA(new TypeToken<Iterable<Integer>>() {}))))
.thenReturn(...);
Общий класс соответствия:
package com.arendvr.matchers;
import com.google.common.reflect.TypeToken;
import org.mockito.ArgumentMatcher;
public class InstanceOfGeneric<T> implements ArgumentMatcher<T> {
private final TypeToken<T> typeToken;
private InstanceOfGeneric(TypeToken<T> typeToken) {
this.typeToken = typeToken;
}
public static <T> InstanceOfGeneric<T> isA(TypeToken<T> typeToken) {
return new InstanceOfGeneric<>(typeToken);
}
@Override
public boolean matches(Object item) {
return item != null && typeToken.getRawType().isAssignableFrom(item.getClass());
}
}
Вот что я делаю:
// Cast from Class<Iterable> to Class<Iterable<Integer>> via the raw type.
// This is provably safe due to erasure, but will generate an unchecked warning
// nonetheless, which we suppress.
@SuppressWarnings("unchecked")
Class<Iterable<Integer>> klass
= (Class<Iterable<Integer>>) (Class) Iterable.class;
// later
isA(klass) // <- now this is typesafe
Можете добавить @SuppressWarnings("unchecked")
выше утверждения. Другого пути нет, но если это вас беспокоит, вы можете переместить приведение к вспомогательному методу.
Нет способа сделать это. Для упрощения вы не можете инициализировать эту переменную без предупреждения:
Class<Iterable<Integer>> iterableIntegerClass = ?
Одним из решений может быть использование антипаттерна псевдо-typedef, вы создаете и используете IntegerIterable
интерфейс
interface IntegerIterable extends Iterable<Integer> {}
затем
isA(IntegerIterable.class)
больше не будет выдавать предупреждение. Но вам придется расширить класс, реализующий Iterable
чтобы позволить им инструменты IntegerIterable
:) Например:
public class IntegerArrayList extends ArrayList<Integer> implements IntegerIterable {}
Ммм вкусно...
Итак, я рекомендую вам просто замазать трещины, добавив к вашему методу:
@SuppressWarnings("unchecked")