web-dev-qa-db-ja.com

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
_

動作しない。

助言がありますか?

28
Mike

Mockito/Hamcrestおよびジェネリッククラス

はい、これはMockito/Hamcrestの一般的な問題です。一般的に、ジェネリッククラスでisA()を使用すると警告が発生します。

最も一般的なジェネリッククラスには、定義済みのMockitoマッチャーがあります: anyList()anyMap()anySet()およびanyCollection()

提案:

mockito 2.1.0のanyIterable()

Mockito 2.1.0では、Iterableを照合するための新しい anyIterable() メソッドが追加されました。

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

Eclipseで無視

Eclipseの警告を取り除きたいだけの場合。オプションは Eclipse Indigo 以降に存在します:

ウィンドウ>設定> Java>コンパイラ>エラー/警告>ジェネリック型>不可避のジェネリック型の問題を無視

@SuppressWarningsによるクイックフィックス

問題が1回だけ発生する場合は、これを行うことをお勧めします。個人的にはisA(Iterable.class)が必要だったことを覚えていません。

Daniel Prydenが言うように、@SuppressWarningsをローカル変数またはヘルパーメソッドに制限できます。

TypeTokenでジェネリックisA()マッチャーを使用する

これは問題を完全に解決します。ただし、次の2つの欠点があります。

  • 構文はきれいすぎず、一部の人々を混乱させるかもしれません。
  • TypeTokenクラスを提供するライブラリへの追加の依存関係があります。ここでは GuavaのTypeTokenクラス を使用しました。 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());
    }
}
28

これが私がすることです:

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

ステートメントの上に@SuppressWarnings("unchecked")を追加できます。他に方法はありませんが、気になる場合は、キャストをヘルパーメソッドに移動できます。

3
Garrett Hall

これを行う方法はありません。簡単にするために、警告なしにこの変数を初期化することはできません:

Class<Iterable<Integer>> iterableIntegerClass = ?

解決策の1つは、 pseudo-typedef antipattern 、を使用することです。IntegerIterableインターフェイスを作成して使用します

interface IntegerIterable extends Iterable<Integer> {}

その後

isA(IntegerIterable.class)

警告を生成しなくなります。ただし、Iterableを実装するクラスを拡張して、IntegerIterableを実装できるようにする必要があります。例:

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

うーんおいしい...

だから、私はあなたの方法に追加することにより、亀裂を紙で覆うことを検討することをお勧めします:

@SuppressWarnings("unchecked")
2
gontard