有没有一种用泛型参数模拟类的干净方法?假设我必须模拟一个类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());
解决此问题的另一种方法是改用@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);
}
我同意不应该在类或方法中抑制警告,因为可能会忽略其他意外抑制的警告。但是恕我直言,禁止仅影响一行代码的警告是绝对合理的。
@SuppressWarnings("unchecked")
Foo<Bar> mockFoo = mock(Foo.class);
正如提到的其他答案,没有不安全的泛型访问和/或抑制泛型警告的直接使用mock()
&方法的好方法。spy()
Mockito 项目 ( #1531 ) 目前存在一个未解决的问题,以添加对使用mock()
&spy()
方法的支持而没有泛型警告。该问题于 2018 年 11 月开放,但没有任何迹象表明它会被优先处理。根据 Mockito 贡献者对该问题的评论之一:
鉴于这
.class
不能很好地与泛型配合使用,我认为我们在 Mockito 中没有任何解决方案。您已经可以这样做@Mock
(JUnit5 扩展也允许方法参数@Mock
s),这应该是一个合适的替代方案。因此,我们可以让这个问题保持开放,但它不太可能被修复,因为这@Mock
是一个更好的 API。
对于 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 。
这是一个有趣的案例:方法接收泛型集合并返回相同基类型的泛型集合。例如:
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>();
}
});
JUnit5
:@ExtendWith(MockitoExtension.class)
在测试类上使用,然后添加此字段:
@Mock
Foo<Bar> barMock;
为什么不使用间谍
var mock = spy(new Foo<Bar>());
when(mockFoo.getValue()).thenReturn(new Bar());