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