Mockitoを使用してジェネリックパラメーターでクラスをモックする
質問
ジェネリックパラメータを使用してクラスをモックするクリーンな方法はありますか?たとえば、Foo<T>
を期待するメソッドに渡す必要があるクラスFoo<Bar>
をモックする必要があるとします。次のことが簡単にできます:
Foo mockFoo = mock(Foo.class);
when(mockFoo.getValue).thenReturn(new Bar());
getValue()
と仮定すると、ジェネリック型T
が返されます。しかし、後で<=>を期待するメソッドに渡すと、子猫が発生します。これを行う唯一の手段はキャストですか?
解決
キャストする必要があると思いますが、それほど悪くないはずです:
Foo<Bar> mockFoo = (Foo<Bar>) mock(Foo.class);
when(mockFoo.getValue).thenReturn(new Bar());
他のヒント
これを回避するもう1つの方法は、代わりに@Mock
注釈を使用することです。
すべての場合に機能するわけではありませんが、よりセクシーに見えます:)
例を次に示します。
@RunWith(MockitoJUnitRunner.class)
public class FooTests {
@Mock
public Foo<Bar> fooMock;
@Test
public void testFoo() {
when(fooMock.getValue()).thenReturn(new Bar());
}
}
MockitoJUnitRunner
は注釈付きのフィールドを初期化します <=> 。
指定したいジェネリック型を満たす中間クラス/インターフェースをいつでも作成できます。たとえば、Fooがインターフェイスである場合、テストクラスに次のインターフェイスを作成できます。
private interface FooBar extends Foo<Bar>
{
}
Fooが非最終クラスである状況では、次のコードでクラスを拡張し、同じことを行うことができます。
public class FooBar extends Foo<Bar>
{
}
その後、次のコードで上記の例を使用できます:
Foo<Bar> mockFoo = mock(FooBar.class);
when(mockFoo.getValue()).thenReturn(new Bar());
テストユーティリティメソッドを作成します。複数回必要な場合に特に便利です。
@Test
public void testMyTest() {
// ...
Foo<Bar> mockFooBar = mockFoo();
when(mockFooBar.getValue).thenReturn(new Bar());
Foo<Baz> mockFooBaz = mockFoo();
when(mockFooBaz.getValue).thenReturn(new Baz());
Foo<Qux> mockFooQux = mockFoo();
when(mockFooQux.getValue).thenReturn(new Qux());
// ...
}
@SuppressWarnings("unchecked") // still needed :( but just once :)
private <T> Foo<T> mockFoo() {
return mock(Foo.class);
}
興味深いケースがあります:メソッドはジェネリックコレクションを受け取り、同じベースタイプのジェネリックコレクションを返します。例:
Collection<? extends Assertion> map(Collection<? extends Assertion> assertions);
このメソッドは、Mockito anyCollectionOfマッチャーとアンサーの組み合わせでモックできます。
when(mockedObject.map(anyCollectionOf(Assertion.class))).thenAnswer(
new Answer<Collection<Assertion>>() {
@Override
public Collection<Assertion> answer(InvocationOnMock invocation) throws Throwable {
return new ArrayList<Assertion>();
}
});
他の誤って抑制された警告を見落とす可能性があるため、クラスまたはメソッドの警告を抑制するべきではないことに同意します。しかし、私見では、1行のコードだけに影響する警告を抑制することは絶対に合理的です。
@SuppressWarnings("unchecked")
Foo<Bar> mockFoo = mock(Foo.class);