如何使用mockito捕获特定类型的列表

时间:2011-04-09 17:17:25

标签: java unit-testing junit mockito

有没有办法使用mockitos ArgumentCaptore捕获特定类型的列表。这不起作用:

ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(ArrayList.class);

8 个答案:

答案 0 :(得分:449)

使用@Captor annotation

可以避免嵌套的泛型问题
@RunWith(MockitoJUnitRunner.class)
public class Test{

    @Mock
    private Service service;

    @Captor
    private ArgumentCaptor<ArrayList<SomeType>> captor;

    @Before
    public void init(){
        MockitoAnnotations.initMocks(this);
    }

    @Test 
    public void shouldDoStuffWithListValues() {
        //...
        verify(service).doStuff(captor.capture()));
    }
}

答案 1 :(得分:121)

是的,这是一个普遍的泛型问题,而不是特定于模仿的问题。

ArrayList<SomeType>没有类对象,因此您无法将此类对象类型安全地传递给需要Class<ArrayList<SomeType>>的方法。

您可以将对象强制转换为正确的类型:

Class<ArrayList<SomeType>> listClass =
              (Class<ArrayList<SomeType>>)(Class)ArrayList.class;
ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(listClass);

这会给出一些关于不安全演员表的警告,当然你的ArgumentCaptor无法区分ArrayList<SomeType>ArrayList<AnotherType>而不检查元素。

(正如在另一个答案中所提到的,虽然这是一个普遍的泛型问题,但是对于带有@Captor注释的类型安全问题,存在针对Mockito的特定解决方案。它仍然无法区分{ {1}}和ArrayList<SomeType>。)

编辑:

另请参阅tenshi的评论。您可以将原始代码从Paŭlo Ebermann更改为此(更简单)

ArrayList<OtherType>

答案 2 :(得分:13)

如果你不害怕旧的java风格(非类型安全通用)语义,这也很有效,并且相当简单&#39; ish:

ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
verify(subject.method(argument.capture()); // run your code
List<SomeType> list = argument.getValue(); // first captured List, etc.

答案 3 :(得分:7)

List<String> mockedList = mock(List.class);

List<String> l = new ArrayList();
l.add("someElement");

mockedList.addAll(l);

ArgumentCaptor<List> argumentCaptor = ArgumentCaptor.forClass(List.class);

verify(mockedList).addAll(argumentCaptor.capture());

List<String> capturedArgument = argumentCaptor.<List<String>>getValue();

assertThat(capturedArgument, hasItem("someElement"));

答案 4 :(得分:2)

基于@ tenshi&@ pkalinow的评论(也是对@rogerdpack的赞誉),以下是创建列表参数captor的简单解决方案,该方法也禁用&#34;使用未经检查或不安全的操作&#34; 警告:

@SuppressWarnings("unchecked")
final ArgumentCaptor<List<SomeType>> someTypeListArgumentCaptor =
    ArgumentCaptor.forClass(List.class);

完整示例here以及相应的传递CI构建和测试运行here

我们的团队在我们的单元测试中已经使用了一段时间,这对我们来说似乎是最直接的解决方案。

答案 5 :(得分:1)

我在Android应用中测试活动时遇到了同样的问题。我使用ActivityInstrumentationTestCase2MockitoAnnotations.initMocks(this);无效。 我用另一个具有相应字段的类解决了这个问题。例如:

class CaptorHolder {

        @Captor
        ArgumentCaptor<Callback<AuthResponse>> captor;

        public CaptorHolder() {
            MockitoAnnotations.initMocks(this);
        }
    }

然后,在活动测试方法中:

HubstaffService hubstaffService = mock(HubstaffService.class);
fragment.setHubstaffService(hubstaffService);

CaptorHolder captorHolder = new CaptorHolder();
ArgumentCaptor<Callback<AuthResponse>> captor = captorHolder.captor;

onView(withId(R.id.signInBtn))
        .perform(click());

verify(hubstaffService).authorize(anyString(), anyString(), captor.capture());
Callback<AuthResponse> callback = captor.getValue();

答案 6 :(得分:1)

对于早期版本的junit,您可以这样做

Class<Map<String, String>> mapClass = (Class) Map.class;
ArgumentCaptor<Map<String, String>> mapCaptor = ArgumentCaptor.forClass(mapClass);

答案 7 :(得分:0)

关于这个确切的问题,有open issue in Mockito's GitHub

我发现了一个简单的解决方法,它不会强迫您在测试中使用注释:

import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.MockitoAnnotations;

public final class MockitoCaptorExtensions {

    public static <T> ArgumentCaptor<T> captorFor(final CaptorTypeReference<T> argumentTypeReference) {
        return new CaptorContainer<T>().captor;
    }

    public static <T> ArgumentCaptor<T> captorFor(final Class<T> argumentClass) {
        return ArgumentCaptor.forClass(argumentClass);
    }

    public interface CaptorTypeReference<T> {

        static <T> CaptorTypeReference<T> genericType() {
            return new CaptorTypeReference<T>() {
            };
        }

        default T nullOfGenericType() {
            return null;
        }

    }

    private static final class CaptorContainer<T> {

        @Captor
        private ArgumentCaptor<T> captor;

        private CaptorContainer() {
            MockitoAnnotations.initMocks(this);
        }

    }

}

这里发生的是,我们创建了一个带有@Captor批注的新类 ,并将捕获器注入其中。然后,我们只需提取captor并从我们的静态方法中将其返回。

在测试中,您可以像这样使用它:

ArgumentCaptor<Supplier<Set<List<Object>>>> fancyCaptor = captorFor(genericType());

或者使用类似于杰克逊的TypeReference的语法:

ArgumentCaptor<Supplier<Set<List<Object>>>> fancyCaptor = captorFor(
    new CaptorTypeReference<Supplier<Set<List<Object>>>>() {
    }
);

它有效,因为Mockito实际上不需要任何类型信息(例如,不同于序列化程序)。

相关问题