Mockito isA(Class<T> clazz) 如何解决类型安全问题?

2022-09-01 09:47:58

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

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

isA(Iterable.class)生成警告,指出它需要未经检查的转换才能符合 。它的语法是什么?Iterable<Integer>

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

不起作用。

有什么建议吗?


答案 1

Mockito/Hamcrest 和泛型类

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

对于最常见的泛型类,有预先定义的 Mockito 匹配器:anyList()、和 .anyMap()anySet()anyCollection()

建议:

anyIterable() in Mockito 2.1.0

Mockito 2.1.0 添加了一个新的 anyIterable() 方法来匹配迭代对象:

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

Eclipse 中的 Ignore

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

窗口>首选项 > Java > 编译器 > >泛型类型的错误/警告 > 忽略不可避免的泛型类型问题

使用@SuppressWarnings快速修复

如果您只有一次问题,我建议您执行此操作。我个人不记得曾经需要过.isA(Iterable.class)

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

将泛型 isA() 匹配器与 TypeToken 一起使用

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

  • 语法不太漂亮,可能会让一些人感到困惑。
  • 您对提供该类的库有额外的依赖关系。在这里,我使用了番石榴的TypeToken类。Gson中还有一门课和JAX-RS课。TypeTokenTypeTokenGenericType

使用通用匹配器:

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

答案 2

以下是我的工作:

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

推荐