350

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

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

假设getValue()返回泛型类型T。但是当我稍后将它传递给一个期待的方法时,那将会有小猫Foo<Bar>。铸造是这样做的唯一方法吗?

4

10 回答 10

337

我认为你确实需要投射它,但它不应该太糟糕:

Foo<Bar> mockFoo = (Foo<Bar>) mock(Foo.class);
when(mockFoo.getValue()).thenReturn(new Bar());
于 2009-10-30T23:12:38.040 回答
323

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

这是一个例子:

@RunWith(MockitoJUnitRunner.class)
public class FooTests {

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

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

于 2011-04-16T15:00:12.927 回答
54

您始终可以创建一个满足您要指定的泛型类型的中间类/接口。例如,如果 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());
于 2014-04-28T19:04:08.203 回答
21

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

@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);
}
于 2015-09-16T18:11:33.477 回答
13

我同意不应该在类或方法中抑制警告,因为可能会忽略其他意外抑制的警告。但是恕我直言,禁止仅影响一行代码的警告是绝对合理的。

@SuppressWarnings("unchecked")
Foo<Bar> mockFoo = mock(Foo.class);
于 2019-01-25T14:41:50.137 回答
7

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

Mockito 项目 ( #1531 ) 目前存在一个未解决的问题,以添加对使用mock()&spy()方法的支持而没有泛型警告。该问题于 2018 年 11 月开放,但没有任何迹象表明它会被优先处理。根据 Mockito 贡献者对该问题的评论之一:

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

于 2020-09-22T15:26:29.533 回答
5

对于 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 。

于 2021-01-27T21:26:04.940 回答
4

这是一个有趣的案例:方法接收泛型集合并返回相同基类型的泛型集合。例如:

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>();
         }
     });
于 2015-09-03T11:59:57.853 回答
3

JUnit5@ExtendWith(MockitoExtension.class)在测试类上使用,然后添加此字段:

@Mock
Foo<Bar> barMock;
于 2021-03-09T14:59:25.500 回答
0

为什么不使用间谍

var mock = spy(new Foo<Bar>());
when(mockFoo.getValue()).thenReturn(new Bar());
于 2021-05-19T17:58:54.827 回答