ジェネリックパラメーターでクラスをモックするクリーンな方法はありますか? Foo<T>
を期待するメソッドに渡す必要があるクラスFoo<Bar>
をモック化する必要があるとします。次のことが簡単にできます。
Foo mockFoo = mock(Foo.class);
when(mockFoo.getValue).thenReturn(new Bar());
getValue()
がジェネリック型T
を返すと仮定します。しかし、後でFoo<Bar>
を期待するメソッドに渡すと、子猫が発生します。これを行う唯一の手段はキャストですか?
あなたはそれをキャストする必要があると思いますが、それはそれほど悪くないはずです:
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
は、 @Mock
アノテーションが付けられたフィールドを初期化します。
指定したいジェネリック型を満たす中間クラス/インターフェースをいつでも作成できます。たとえば、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);
}
他の誤って抑制された警告を見落とす可能性があるため、クラスまたはメソッドで警告を抑制するべきではないことに同意します。しかし、私見では、1行のコードのみに影響する警告を抑制することは絶対に合理的です。
@SuppressWarnings("unchecked")
Foo<Bar> mockFoo = mock(Foo.class);
興味深いケースがあります:メソッドはジェネリックコレクションを受け取り、同じベースタイプのジェネリックコレクションを返します。例えば:
Collection<? extends Assertion> map(Collection<? extends Assertion> assertions);
このメソッドは、Mockito anyCollectionOfマッチャーとAnswerの組み合わせでモックできます。
when(mockedObject.map(anyCollectionOf(Assertion.class))).thenAnswer(
new Answer<Collection<Assertion>>() {
@Override
public Collection<Assertion> answer(InvocationOnMock invocation) throws Throwable {
return new ArrayList<Assertion>();
}
});