是否有一种干净的方法来用泛型参数模拟类?假设我必须模拟一个类Foo<T>,我需要传递到一个方法,期望Foo<Bar>。我可以很容易地做到以下几点:

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

假设getValue()返回泛型类型t,但当我后来将它传递给一个期望Foo<Bar>的方法时,它将有小猫。选角是唯一的方法吗?


当前回答

(在我看来)最简单和最易读的方法是使用方法级注入。

这将导致在测试方法中拥有所有测试数据。这将使您的测试类保持干净,因为没有“浮动”mock。

@ExtendWith(MockitoExtension.class)
public class SomeClassTest {

    @Test
    void someTestMethod(@Mock Foo<Bar> fooMock) {
        // do something with your mock
    }
    
}

其他回答

所以你得到了这个:

Foo mockFoo = mock(Foo.class);

解决方法,从我最不喜欢的开始到最喜欢的:

使用@SuppressWarnings("unchecked")注释。这并不能真正解决问题,但你将不再收到警告。

@SuppressWarnings("unchecked")
Foo mockFoo = mock(Foo.class);
when(mockFoo.getValue).thenReturn(new Bar());

把它。不幸的是,它仍然给出了警告。所以你也需要在这里使用注释:

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

使用@Mock注释。不会有任何警告。这里,什么时候可以加入到实际测试中。

@Mock
public Foo<Bar> fooMock;

使用@MockBean注释。这将直接创建一个模拟bean。没有警告。

@MockBean
public Foo<Bar> fooMock;

正如其他答案所提到的,如果没有不安全的泛型访问和/或抑制泛型警告,直接使用mock()和spy()方法并不是一个很好的方法。

目前在Mockito项目(#1531)中有一个悬而未决的问题,即添加对使用mock()和spy()方法而不带泛型警告的支持。该问题于2018年11月开始发行,但没有任何迹象表明它将被优先考虑。根据Mockito贡献者对这个问题的评论:

鉴于.class不能很好地使用泛型,我认为在Mockito中没有任何解决方案。您已经可以使用@Mock (JUnit5扩展还允许方法参数@Mock),这应该是一个合适的替代方案。因此,我们可以保持这个问题的开放性,但它不太可能被修复,因为@Mock是一个更好的API。

创建一个测试工具方法。特别有用,如果你需要它不止一次。

@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);
}

另一种解决方法是使用@Mock注释。 不是所有情况下都有效,但看起来更性感:)

这里有一个例子:

@RunWith(MockitoJUnitRunner.class)
public class FooTests {

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

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

为什么不用间谍

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