使用Mockito模拟具有泛型参数的类

时间:2009-10-30 22:48:53

标签: java generics mockito

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

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

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

9 个答案:

答案 0 :(得分:240)

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

以下是一个例子:

@RunWith(MockitoJUnitRunner.class)
public class FooTests {

    @Mock
    public Foo<Bar> fooMock;

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

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

答案 1 :(得分:234)

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

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

答案 2 :(得分:30)

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

答案 3 :(得分:12)

创建测试实用程序方法。如果你不止一次需要它,特别有用。

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

答案 4 :(得分:3)

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

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

答案 5 :(得分:3)

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

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

答案 6 :(得分:2)

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

Mockito项目(#1531)当前存在一个未解决的问题,它增加了对使用mock()spy()方法的支持,而没有泛型警告。该问题已于2018年11月开始,但没有迹象表明它是否会被优先考虑。

答案 7 :(得分:1)

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

答案 8 :(得分:1)

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

@Mock
Foo<Bar> barMock;