是否有一种干净的方法来用泛型参数模拟类?假设我必须模拟一个类Foo<T>,我需要传递到一个方法,期望Foo<Bar>。我可以很容易地做到以下几点:
Foo mockFoo = mock(Foo.class);
when(mockFoo.getValue).thenReturn(new Bar());
假设getValue()返回泛型类型t,但当我后来将它传递给一个期望Foo<Bar>的方法时,它将有小猫。选角是唯一的方法吗?
是否有一种干净的方法来用泛型参数模拟类?假设我必须模拟一个类Foo<T>,我需要传递到一个方法,期望Foo<Bar>。我可以很容易地做到以下几点:
Foo mockFoo = mock(Foo.class);
when(mockFoo.getValue).thenReturn(new Bar());
假设getValue()返回泛型类型t,但当我后来将它传递给一个期望Foo<Bar>的方法时,它将有小猫。选角是唯一的方法吗?
当前回答
您总是可以创建一个中间类/接口来满足您想要指定的泛型类型。例如,如果Foo是一个接口,您可以在测试类中创建以下接口。
private interface FooBar extends Foo<Bar>
{
}
在Foo是一个非final类的情况下,你可以用下面的代码扩展这个类,并做同样的事情:
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);
}
对于JUnit5,我认为最好的方法是在方法参数或字段中使用@Mock @ExtendWith(MockitoExtension.class)。
下面的例子用Hamcrest匹配器演示了这一点。
package com.vogella.junit5;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasItem;
import static org.mockito.Mockito.verify;
import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class MockitoArgumentCaptureTest {
@Captor
private ArgumentCaptor<List<String>> captor;
@Test
public final void shouldContainCertainListItem(@Mock List<String> mockedList) {
var asList = Arrays.asList("someElement_test", "someElement");
mockedList.addAll(asList);
verify(mockedList).addAll(captor.capture());
List<String> capturedArgument = captor.getValue();
assertThat(capturedArgument, hasItem("someElement"));
}
}
有关所需的Maven/Gradle依赖项,请参阅https://www.vogella.com/tutorials/Mockito/article.html。
为什么不用间谍
var mock = spy(new Foo<Bar>());
when(mockFoo.getValue()).thenReturn(new Bar());
这里有一个有趣的例子:方法接收泛型集合并返回相同基类型的泛型集合。例如:
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>();
}
});
另一种解决方法是使用@Mock注释。 不是所有情况下都有效,但看起来更性感:)
这里有一个例子:
@RunWith(MockitoJUnitRunner.class)
public class FooTests {
@Mock
public Foo<Bar> fooMock;
@Test
public void testFoo() {
when(fooMock.getValue()).thenReturn(new Bar());
}
}
MockitoJUnitRunner初始化带有@Mock注释的字段。