Comment capturer une liste de type spécifique avec mockito

Existe-t-il un moyen de capturer une liste de type spécifique en utilisant mockitos ArgumentCaptore. Cela ne fonctionne pas:

ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(ArrayList.class);
219
demandé sur rogerdpack 2011-04-09 21:17:25

6 réponses

Le problème des génériques imbriqués peut être évité avec l'annotation @Captor :

@RunWith(MockitoJUnitRunner.class)
public class Test{

    @Mock
    private Service service;

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

    @Test 
    public void shouldDoStuffWithListValues() {
        //...
        verify(service).doStuff(captor.capture()));
    }
}
389
répondu crunchdog 2016-08-05 16:29:40

Ouais, c'est un problème Générique général, pas spécifique à mockito.

Il N'y a pas d'objet de classe pour ArrayList<SomeType>, et donc vous ne pouvez pas passer en toute sécurité un tel objet à une méthode nécessitant un Class<ArrayList<SomeType>>.

Vous pouvez convertir l'objet au bon type:

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

Cela donnera quelques avertissements sur les lancements dangereux, et bien sûr votre ArgumentCaptor ne peut pas vraiment faire la différence entre ArrayList<SomeType> et ArrayList<AnotherType> Sans peut-être inspecter les éléments.

(comme mentionné dans l'autre réponse, bien qu'il s'agisse d'un problème Générique général, il existe une solution spécifique à Mockito pour le problème de sécurité de type avec l'annotation @Captor. Il ne peut toujours pas faire la distinction entre un ArrayList<SomeType> et ArrayList<OtherType>.)

Modifier:

Jetez également un oeil au commentaire de tenshi. Vous pouvez changer le code d'origine de Palolo Ebermann à ceci (beaucoup plus simple)

final ArgumentCaptor<List<SomeType>> listCaptor
        = ArgumentCaptor.forClass((Class) List.class);
109
répondu Paŭlo Ebermann 2017-04-18 14:55:11

Si vous n'avez pas peur de l'ancienne sémantique de type java (non type safe generic), cela fonctionne également et est raisonnablement simple:

ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
verify(subject.method(argument.capture()); // run your code
List<SomeType> list = argument.getValue(); // first captured List, etc.
11
répondu rogerdpack 2018-05-19 02:11:30
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"));
6
répondu kkmike999 2017-09-11 10:11:00

Basé sur les commentaires de @tenshi et @pkalinow (également bravo à @rogerdpack), ce qui suit est une solution simple pour créer un argument de liste captor qui désactive également le"utilise des opérations non cochées ou non sécurisées" avertissement:

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

Exemple complet ici {[7] } et la construction et l'exécution de test CI correspondantes ici .

Notre équipe l'utilise depuis un certain temps dans nos tests unitaires et cela semble être la solution la plus simple pour nous.

1
répondu mrts 2018-05-30 17:17:36

J'ai eu le même problème avec l'activité de test dans mon application Android. J'ai utilisé ActivityInstrumentationTestCase2 et MockitoAnnotations.initMocks(this); n'a pas fonctionné. J'ai résolu ce problème avec une autre classe avec respectivement domaine. Par exemple:

class CaptorHolder {

        @Captor
        ArgumentCaptor<Callback<AuthResponse>> captor;

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

Ensuite, dans la méthode d'essai d'activité:

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();
0
répondu Timofey Orischenko 2015-08-24 09:48:14