使用Mockito模拟具有通用参数的类


280

有没有一种使用通用参数模拟类的干净方法?说我必须模拟一个类Foo<T>,我需要将该类传递给期望使用的方法Foo<Bar>。我可以很容易地做到以下几点:

Foo mockFoo = mock(Foo.class);
when(mockFoo.getValue).thenReturn(new Bar());

假定getValue()返回通用类型T。但是,当我稍后将其传递给期望的方法时,将会有小猫Foo<Bar>。铸造是这样做的唯一方法吗?

Answers:


280

我认为您确实需要强制转换,但应该不会太糟:

Foo<Bar> mockFoo = (Foo<Bar>) mock(Foo.class);
when(mockFoo.getValue()).thenReturn(new Bar());

34
是的,但是您仍然会有警告。有可能避免警告吗?
odwl 2010年

12
@SuppressWarnings(“ unchecked”)
qualidafial 2010年

18
我认为这完全可以接受,因为我们在单元测试中谈论的是模拟对象。
Magnilex 2014年

1
@demaniak根本不起作用。在该上下文中不能使用参数匹配器。
克日什托夫·Krasoń

1
@demaniak可以很好地编译,但是在运行测试时,它将引发InvalidUseOfMatchersException(这是RuntimeException)
Superole

277

解决此问题的另一种方法是改用@Mock注释。并非在所有情况下都有效,但看起来却更性感:)

这是一个例子:

@RunWith(MockitoJUnitRunner.class)
public class FooTests {

    @Mock
    public Foo<Bar> fooMock;

    @Test
    public void testFoo() {
        when(fooMock.getValue()).thenReturn(new Bar());
    }
}

MockitoJUnitRunner初始化带注释的字段@Mock


3
在1.9.5中已弃用。:(似乎更清洁了我。
代码见习期

12
@CodeNovitiate我在1.9.5的MockitoJUnitRunner和Mock上找不到任何弃用注释。那么,不赞成使用什么?(是的,org.mockito.MockitoAnnotations.Mock已过时,但您应该使用org.mockito.Mock代替)
neu242 2014年

12
做得好,这对我来说非常有效。这不仅是“性感”,而且无需使用就能避免警告SuppressWarnings。存在警告是有原因的,最好不要养成抑制它们的习惯。谢谢!
妮可

4
我不喜欢使用@Mock代替的一件事mock():在构造期间字段仍然为null,因此我当时无法插入依赖项,也不能使字段成为最终字段。前者当然可以通过带@Before注释的方法来解决。
鲁迪格·舒尔茨

3
对于初始化,只需调用MockitoAnnotations.initMocks(this);即可。
borjab '16

42

您总是可以创建一个满足您要指定的通用类型的中间类/接口。例如,如果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());

4
提供Foo一个接口或非最终类,这似乎是一个合理的解决方案。谢谢。
Tim Clemons 2014年

我更新了答案,以包括非最终课程的示例。理想情况下,您将针对接口进行编码,但情况并非总是如此。接得好!
dsingleton 2014年

16

创建一个测试实用程序方法。如果您需要多次使用,则特别有用。

@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
@WilliamDutton static <T> T genericMock(Class<? super T> classToMock) { return (T)mock(classToMock); }它甚至不需要任何抑制:)但是要小心,Integer num = genericMock(Number.class)编译时会抛出异常ClassCastException。这仅在最常见的G<P> mock = mock(G.class)情况下有用。
TWiStErRob'7

6

我同意,一个人不应在类或方法中禁止显示警告,因为这样可能会忽略其他偶然被禁止的警告。但是恕我直言,禁止仅影响一行代码的警告是绝对合理的。

@SuppressWarnings("unchecked")
Foo<Bar> mockFoo = mock(Foo.class);

3

这是一个有趣的情况:方法接收通用集合并返回相同基本类型的通用集合。例如:

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>();
         }
     });
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.