Mockito是A(Class <t> clazz)如何解决类型安全?</t>

时间:2012-08-17 19:46:30

标签: java junit mockito java-5

在我的测试中我有以下几行:

when(client.runTask(anyString(), anyString(), isA(Iterable.class)).thenReturn(...)

isA(Iterable.class)会产生警告,表示需要取消选中转换才能符合Iterable<Integer>。这是什么语法?

isA(Iterable<Integer>.class)
isA((Iterable<Integer>)Iterable.class

不起作用。

有什么建议吗?

4 个答案:

答案 0 :(得分:28)

Mockito / Hamcrest和泛型类

是的,这是Mockito / Hamcrest的普遍问题。通常,将isA()与泛型类一起使用会产生警告。

对于最常见的通用类,存在预定的Mockito匹配器:anyList()anyMap()anySet()anyCollection()

建议:

Mockito 2.1.0中的

anyIterable()

Mockito 2.1.0添加了一个新的anyIterable()方法来匹配Iterables:

when(client.runTask(anyString(), anyString(), anyIterable()).thenReturn(...)

在Eclipse中忽略

如果你只想摆脱Eclipse中的警告。自Eclipse Indigo以来存在选项:

  

窗口&gt;偏好&gt; Java&gt;编译器&gt;错误/警告&gt;通用   类型&gt;忽略不可避免的泛型类型问题

使用@SuppressWarnings快速修复

如果您只遇到一次问题,我建议您这样做。我个人不记得曾经需要isA(Iterable.class)

正如Daniel Pryden所说,您可以将@SuppressWarnings限制为局部变量或辅助方法。

使用带有TypeToken

的通用isA()匹配器

这解决了这个问题。但它有两个缺点:

  • 语法不太漂亮,可能会让一些人感到困惑。
  • 您对提供TypeToken类的库有额外的依赖关系。在这里,我使用了TypeToken class from Guava。 Gson中还有一个TypeToken类,JAX-RS中有一个GenericType

使用通用匹配器:

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());
    }
}

答案 1 :(得分:7)

这就是我的所作所为:

// 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

答案 2 :(得分:3)

您可以在语句上方添加@SuppressWarnings("unchecked")。没有其他办法,但如果它困扰你,你可以将演员阵容移动到辅助方法。

答案 3 :(得分:2)

没有办法做到这一点。为简化起见,您无法在不发出警告的情况下初始化此变量:

Class<Iterable<Integer>> iterableIntegerClass = ?

一种解决方案可能是使用pseudo-typedef antipattern, ,您创建并使用IntegerIterable界面

interface IntegerIterable extends Iterable<Integer> {}

然后

isA(IntegerIterable.class)

将不再发出警告。但是你必须扩展实现Iterable的类,让它们实现IntegerIterable :)例如:

public class IntegerArrayList extends ArrayList<Integer> implements IntegerIterable {}

嗯好吃......

所以,我会鼓励你考虑通过添加你的方法来解决问题:

@SuppressWarnings("unchecked")