Как захватить список определенного типа с помощью mockito - PullRequest
256 голосов
/ 09 апреля 2011

Есть ли способ захвата списка определенного типа с помощью mockitos ArgumentCaptore. Это не работает:

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

Ответы [ 7 ]

460 голосов
/ 14 апреля 2011

Вложенных неполадок дженериков можно избежать с помощью аннотации @ Captor :

@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()));
    }
}
124 голосов
/ 09 апреля 2011

Да, это общая проблема генериков, а не специфическая для mockito.

Не существует объекта класса для 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> без проверки элементов.

(Как уже упоминалось в другом ответе, хотя это общая проблема обобщения, существует специфическое для Mockito решение проблемы безопасности типов с аннотацией @Captor. Он по-прежнему не может различить ArrayList<SomeType> и ArrayList<OtherType>.)

Edit:

Взгляните также на комментарий тенши . Вы можете изменить исходный код с Paŭlo Ebermann на этот (намного проще)

final ArgumentCaptor<List<SomeType>> listCaptor
        = ArgumentCaptor.forClass((Class) List.class);
13 голосов
/ 02 декабря 2014

Если вы не боитесь старой семантики в стиле java (не являющейся типичной безопасностью), это также работает и достаточно просто:

ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
verify(subject.method(argument.capture()); // run your code
List<SomeType> list = argument.getValue(); // first captured List, etc.
8 голосов
/ 19 апреля 2016
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"));
2 голосов
/ 30 мая 2018

Основываясь на комментариях @enshi и @Pkalinow (также слава @rogerdpack), ниже приведено простое решение для создания захватчика аргументов списка, который также отключает «использует непроверенные или небезопасные операции» предупреждение:

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

Полный пример здесь и соответствующий проход сборки CI и тестовый прогон здесь .

Наша команда уже некоторое время использует это внаши модульные тесты, и это выглядит как самое простое решение для нас.

1 голос
/ 16 января 2019

Для более ранней версии junit вы можете сделать

Class<Map<String, String>> mapClass = (Class) Map.class;
ArgumentCaptor<Map<String, String>> mapCaptor = ArgumentCaptor.forClass(mapClass);
1 голос
/ 24 августа 2015

У меня была та же проблема с тестированием в моем приложении для Android. Я использовал ActivityInstrumentationTestCase2 и MockitoAnnotations.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();
...