Czy istnieje sposób na przechwycenie listy określonego typu przy użyciu mockitos ArgumentCaptore. To nie działa:
ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(ArrayList.class);
Czy istnieje sposób na przechwycenie listy określonego typu przy użyciu mockitos ArgumentCaptore. To nie działa:
ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(ArrayList.class);
Odpowiedzi:
Zagnieżdżonego problemu rodzajowego można uniknąć za pomocą adnotacji @Captor :
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()));
}
}
MockitoAnnotations.initMocks(this)
w @Before
sposób niż za pomocą gońca, który wyłącza zdolność do użytkowania innego biegacza. Jednak +1, dziękuję za wskazanie adnotacji.
Tak, jest to ogólny problem ogólny, nie związany z mockito.
Nie ma obiektu klasy ArrayList<SomeType>
, a zatem nie można bezpiecznie wpisać takiego obiektu do metody wymagającej Class<ArrayList<SomeType>>
.
Możesz rzucić obiekt na odpowiedni typ:
Class<ArrayList<SomeType>> listClass =
(Class<ArrayList<SomeType>>)(Class)ArrayList.class;
ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(listClass);
To da pewne ostrzeżenia o niebezpiecznych rzutach i oczywiście twój ArgumentCaptor nie może tak naprawdę rozróżniać między elementami ArrayList<SomeType>
i ArrayList<AnotherType>
nie sprawdzać ich.
(Jak wspomniano w drugiej odpowiedzi, chociaż jest to ogólny problem ogólny, istnieje specyficzne dla Mockito rozwiązanie problemu bezpieczeństwa typu z @Captor
adnotacją. Wciąż nie można odróżnić an ArrayList<SomeType>
i ArrayList<OtherType>
.)
Zobacz także komentarz tenshi . Możesz zmienić oryginalny kod z Paŭlo Ebermann na ten (znacznie prostszy)
final ArgumentCaptor<List<SomeType>> listCaptor
= ArgumentCaptor.forClass((Class) List.class);
ArgumentCaptor<List<SimeType>> argument = ArgumentCaptor.forClass((Class) List.class);
@SuppressWarnings("unchecked")
adnotacji nad linią definicji captor argumentu. Ponadto przesyłanie do Class
jest zbędne.
Class
nie jest zbędne w moich testach.
Jeśli nie boisz się starej semantyki w stylu java (nie typowa bezpieczna generyczna), to również działa i jest dość proste:
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"));
W oparciu o komentarze @ tenshi i @ pkalinow (również kudos do @rogerdpack), poniższe jest proste rozwiązanie do tworzenia modułu przechwytującego argument listy, który również wyłącza ostrzeżenie „używa operacji niesprawdzonych lub niebezpiecznych” :
@SuppressWarnings("unchecked")
final ArgumentCaptor<List<SomeType>> someTypeListArgumentCaptor =
ArgumentCaptor.forClass(List.class);
Pełny przykład tutaj i odpowiadająca mu wersja kompilacji CI i uruchomienie testowe tutaj .
Nasz zespół używa tego od jakiegoś czasu w naszych testach jednostkowych i wydaje się to dla nas najprostszym rozwiązaniem.
W przypadku wcześniejszej wersji programu junit możesz to zrobić
Class<Map<String, String>> mapClass = (Class) Map.class;
ArgumentCaptor<Map<String, String>> mapCaptor = ArgumentCaptor.forClass(mapClass);
Miałem ten sam problem z testowaniem aktywności w mojej aplikacji na Androida. Użyłem ActivityInstrumentationTestCase2
i MockitoAnnotations.initMocks(this);
nie działałem. Rozwiązałem ten problem z inną klasą, odpowiednio z polem. Na przykład:
class CaptorHolder {
@Captor
ArgumentCaptor<Callback<AuthResponse>> captor;
public CaptorHolder() {
MockitoAnnotations.initMocks(this);
}
}
Następnie w metodzie testu aktywności:
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();
W GitHub Mockito istnieje otwarty problem dotyczący tego konkretnego problemu.
Znalazłem proste obejście, które nie zmusza cię do używania adnotacji w swoich testach:
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);
}
}
}
Co się dzieje, jest to, że możemy stworzyć nową klasę z tej @Captor
adnotacji i wstrzyknąć captor do niego. Następnie po prostu wydobywamy porywacz i zwracamy go z naszej metody statycznej.
W teście możesz użyć go w następujący sposób:
ArgumentCaptor<Supplier<Set<List<Object>>>> fancyCaptor = captorFor(genericType());
Lub ze składnią podobną do Jacksona TypeReference
:
ArgumentCaptor<Supplier<Set<List<Object>>>> fancyCaptor = captorFor(
new CaptorTypeReference<Supplier<Set<List<Object>>>>() {
}
);
Działa, ponieważ Mockito tak naprawdę nie potrzebuje żadnych informacji o typie (na przykład w przeciwieństwie do serializatorów).
ArrayList
) jest okropnym pomysłem . Zawsze możesz użyćList
interfejsu, a jeśli chcesz przedstawić fakt, że jest on kowariantny, możesz użyćextends
:ArgumentCaptor<? extends List<SomeType>>