是否有一种使用泛型参数模拟类的干净方法?假设我必须模拟一个类Foo<T>
,我需要将其传递给期望Foo<Bar>
的方法。我可以很容易地做到以下几点:
Foo mockFoo = mock(Foo.class);
when(mockFoo.getValue).thenReturn(new Bar());
假设getValue()
返回泛型类型T
。但是当我稍后将其传递给期望Foo<Bar>
的方法时,那将会有小猫。铸造是唯一的方法吗?
答案 0 :(得分:240)
另一种方法是使用@Mock
注释。
在所有情况下都不起作用,但看起来更性感:)
以下是一个例子:
@RunWith(MockitoJUnitRunner.class)
public class FooTests {
@Mock
public Foo<Bar> fooMock;
@Test
public void testFoo() {
when(fooMock.getValue()).thenReturn(new Bar());
}
}
MockitoJUnitRunner
初始化使用@Mock
注释的字段。
答案 1 :(得分:234)
我认为你确实需要施展它,但它不应该太糟糕:
Foo<Bar> mockFoo = (Foo<Bar>) mock(Foo.class);
when(mockFoo.getValue).thenReturn(new Bar());
答案 2 :(得分:30)
您始终可以创建一个中间类/接口,以满足您要指定的泛型类型。例如,如果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());
答案 3 :(得分:12)
创建测试实用程序方法。如果你不止一次需要它,特别有用。
@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);
}
答案 4 :(得分:3)
这是一个有趣的案例:method接收泛型集合并返回相同基类型的泛型集合。例如:
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>();
}
});
答案 5 :(得分:3)
我同意,一个人不应在类或方法中禁止显示警告,因为这样可能会忽略其他偶然被禁止的警告。但是恕我直言,禁止只影响一行代码的警告是绝对合理的。
@SuppressWarnings("unchecked")
Foo<Bar> mockFoo = mock(Foo.class);
答案 6 :(得分:2)
正如提到的其他答案一样,没有一种不使用不安全的泛型访问和/或禁止泛型警告的直接使用mock()
和spy()
方法的好方法。
Mockito项目(#1531)当前存在一个未解决的问题,它增加了对使用mock()
和spy()
方法的支持,而没有泛型警告。该问题已于2018年11月开始,但没有迹象表明它是否会被优先考虑。
答案 7 :(得分:1)
对于 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。
答案 8 :(得分:1)
JUnit5
:在测试类上使用 @ExtendWith(MockitoExtension.class)
然后添加这个字段:
@Mock
Foo<Bar> barMock;