Java 使用Mockito模拟具有泛型参数的类
有没有一种干净的方法可以用泛型参数模拟类?假设我必须模拟一个类Java 使用Mockito模拟具有泛型参数的类,java,generics,mockito,Java,Generics,Mockito,有没有一种干净的方法可以用泛型参数模拟类?假设我必须模拟一个类Foo,我需要将它传递到一个需要Foo的方法中。我可以很容易地做到以下几点: Foo mockFoo = mock(Foo.class); when(mockFoo.getValue).thenReturn(new Bar()); 假设getValue()返回泛型类型T。但是,当我稍后将它传递到一个期望Foo的方法中时,会有小猫。铸造是唯一的方法吗?我认为你确实需要铸造它,但它不应该太糟糕: Foo<Bar> mockF
Foo
,我需要将它传递到一个需要Foo
的方法中。我可以很容易地做到以下几点:
Foo mockFoo = mock(Foo.class);
when(mockFoo.getValue).thenReturn(new Bar());
假设
getValue()
返回泛型类型T
。但是,当我稍后将它传递到一个期望Foo
的方法中时,会有小猫。铸造是唯一的方法吗?我认为你确实需要铸造它,但它不应该太糟糕:
Foo<Bar> mockFoo = (Foo<Bar>) mock(Foo.class);
when(mockFoo.getValue()).thenReturn(new Bar());
foomockfoo=(Foo)mock(Foo.class);
当(mockFoo.getValue())。然后返回(新条());
另一种解决方法是使用@Mock
注释。
并非在所有情况下都有效,但看起来更性感:)
下面是一个例子:
@RunWith(MockitoJUnitRunner.class)
public class FooTests {
@Mock
public Foo<Bar> fooMock;
@Test
public void testFoo() {
when(fooMock.getValue()).thenReturn(new Bar());
}
}
@RunWith(MockitoJUnitRunner.class)
公营足球场{
@嘲弄
公众食物模拟;
@试验
公共void testFoo(){
当(fooMock.getValue()).thenReturn(newbar());
}
}
初始化用注释的字段。您始终可以创建一个满足您想要指定的泛型类型的中间类/接口。例如,如果Foo是一个接口,那么可以在测试类中创建以下接口
private interface FooBar extends Foo<Bar>
{
}
专用接口FooBar扩展了Foo
{
}
在Foo是非final类的情况下,您可以使用以下代码扩展该类并执行相同的操作:
public class FooBar extends Foo<Bar>
{
}
公共类FooBar扩展了Foo
{
}
然后,您可以使用以下代码使用上述任一示例:
Foo<Bar> mockFoo = mock(FooBar.class);
when(mockFoo.getValue()).thenReturn(new Bar());
foomockfoo=mock(FooBar.class);
当(mockFoo.getValue())。然后返回(新条());
这里有一个有趣的例子:方法接收泛型集合并返回相同基类型的泛型集合。例如:
Collection<? extends Assertion> map(Collection<? extends Assertion> assertions);
Collection创建一个测试实用程序方法。特别有用,如果你需要它不止一次
@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);
}
@测试
公共void testMyTest(){
// ...
Foo mockFooBar=mockFoo();
when(mockFooBar.getValue).thenReturn(newbar());
Foo mockFooBaz=mockFoo();
when(mockFooBaz.getValue).thenReturn(new Baz());
Foo mockFooQux=mockFoo();
when(mockFooQux.getValue).thenReturn(new Qux());
// ...
}
@SuppressWarnings(“未选中”)//仍然需要:(但只需一次:)
私人Foo mockFoo(){
返回模拟(Foo.class);
}
我同意,一个人不应该在类或方法中隐藏警告,因为他可能会忽略其他意外隐藏的警告。但是,我认为,禁止只影响一行代码的警告是绝对合理的
@SuppressWarnings("unchecked")
Foo<Bar> mockFoo = mock(Foo.class);
@SuppressWarnings(“未选中”)
Foo mockFoo=mock(Foo.class);
正如其他答案所提到的,没有不安全的泛型访问和/或禁止泛型警告,直接使用mock()
&spy()
方法不是一个好方法
Mockito项目()中目前存在一个未解决的问题,即添加对使用mock()
&spy()
方法的支持,而不使用泛型警告。该问题于2018年11月开始发行,但没有任何迹象表明将优先考虑。根据Mockito撰稿人对该问题的评论:
鉴于.class
不能很好地处理泛型,我认为在Mockito中没有任何解决方案。您已经可以执行@Mock
(JUnit5扩展还允许使用方法参数@Mock
s),这应该是一个合适的替代方法。因此,我们可以保持这个问题的开放性,但它不太可能被修复,因为@Mock
是一个更好的API
对于JUnit5,我认为最好的方法是在方法参数或字段中使用@Mock的@ExtendWith(MockitoExtension.class)
下面的示例演示如何使用Hamcrest matchers
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"));
}
}
package com.vogella.junit5;
导入静态org.hamcrest.matcherasert.assertThat;
导入静态org.hamcrest.Matchers.hasItem;
导入静态org.mockito.mockito.verify;
导入java.util.array;
导入java.util.List;
导入org.junit.jupiter.api.Test;
导入org.junit.jupiter.api.extension.extensedWith;
导入org.mockito.ArgumentCaptor;
导入org.mockito.Captor;
导入org.mockito.Mock;
导入org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
公共类MockitoArgumentCaptureTest{
@俘虏
私家侦探;
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"));
}
}
@Mock
Foo<Bar> barMock;