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;