Mockito ile belirli bir tür liste nasıl yakalanır


301

Mockitos ArgumentCaptore kullanarak belirli türde bir liste yakalamanın bir yolu var mı? Bu işe yaramaz:

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

8
Burada somut liste uygulamasını kullanmanın korkunç bir fikir olduğunu görüyorum ( ArrayList). Her zaman Listarayüzü kullanabilirsiniz ve gerçeği temsil etmek istiyorsanız, bu kovaryant, o zaman kullanabilirsiniz extends:ArgumentCaptor<? extends List<SomeType>>
tenshi

Yanıtlar:


533

@Captor ek açıklamasıyla iç içe geçmiş jenerik sorunlardan kaçınılabilir :

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

70
Ben kullanmayı tercih MockitoAnnotations.initMocks(this)içinde @Beforeyöntemle yerine bir koşucu kullanarak bu dışlayan başka koşucu kullanma yeteneği. Ancak +1, ek açıklamayı işaret ettiğiniz için teşekkür ederiz.
John B

4
Bu örneğin eksiksiz olduğundan emin değilim. Anlıyorum ... Hata: (240, 40) java: değişken captor başlatılmamış olabilir ben aşağıda tenshi'nin cevabını seviyorum
Michael Dausmann

1
Aynı sorunla karşılaştım ve bana yardımcı olan bu blog gönderisini buldum: blog.jdriven.com/2012/10/… . Ek açıklamayı sınıfınıza ekledikten sonra MockitoAnnotations.initMocks kullanma adımını içerir. Fark ettiğim bir şey, yerel bir değişken içinde olamayacağınız.
SlopeOak

1
@ chamzz.dot ArgumentCaptor <ArrayList <SomeType>> kaptor; zaten "SomeType" <- belirli bir tür bir dizi yakalama, değil mi?
Miguel R. Santaella

1
Ben genellikle Captor bildiriminde ArrayList yerine List tercih ederim: ArgumentCaptor <List <SomeType>> captor;
Miguel R. Santaella

146

Evet, bu genel bir jenerik problemdir, mockito'ya özgü değildir.

Sınıf nesnesi yoktur ArrayList<SomeType>ve bu nedenle böyle bir nesneyi a gerektiren bir yönteme güvenli bir şekilde yazamazsınız Class<ArrayList<SomeType>>.

Nesneyi doğru türe atabilirsiniz:

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

Bu, güvenli olmayan dökümler hakkında bazı uyarılar verir ve elbette ArgumentCaptor'ınız arasında gerçekten ayrım yapamaz ArrayList<SomeType>ArrayList<AnotherType> öğeleri incelemeksizin ve teftiş .

(Diğer cevapta belirtildiği gibi, bu genel bir jenerik problem olsa da, @Captorek açıklama ile tip güvenliği sorunu için Mockito'ya özgü bir çözüm vardır .ArrayList<SomeType> ve ArrayList<OtherType>.)

Düzenle:

Ayrıca tenshi'nin yorumuna bir göz atın . Orijinal kodu Paŭlo Ebermann'dan buna değiştirebilirsiniz (çok daha basit)

final ArgumentCaptor<List<SomeType>> listCaptor
        = ArgumentCaptor.forClass((Class) List.class);

49
ArgumentCaptor<List<SimeType>> argument = ArgumentCaptor.forClass((Class) List.class);
Gösterdiğiniz örnek, java'nın

4
Denetlenmeyen veya güvenli olmayan işlemler kullanma uyarısını devre dışı bırakmak için @SuppressWarnings("unchecked")bağımsız değişken yakalama tanımı satırının üst kısmındaki ek açıklamayı kullanın . Ayrıca, döküm yapmak Classgereksizdir.
mrts

1
ClassTestlerimdeki döküm gereksiz değil.
Wim Deblauwe

16

Eski java tarzı (tür güvenli olmayan genel) anlambilimden korkmuyorsanız, bu da işe yarar ve oldukça basittir:

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

2
Uyarıları devre dışı bırakmak için bildirimden önce @SuppressWarnings ("rawtypes") ekleyebilirsiniz.
pkalinow

9
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

@ Tenshi'nin ve @ pkalinow'un yorumlarına dayanarak (ayrıca @rogerdpack için kudos), "denetlenmeyen veya güvenli olmayan işlemleri kullanır" uyarısını da devre dışı bırakan bir liste bağımsız değişkeni oluşturmak için basit bir çözümdür :

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

Tam örnek burada ve karşılık gelen geçen CI inşa ve burada test çalıştırın .

Ekibimiz bunu bir süredir birim testlerimizde kullanıyor ve bu bizim için en basit çözüm gibi görünüyor.


2

Junit'in önceki bir sürümü için şunları yapabilirsiniz:

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

1

Android uygulamamdaki etkinliği test etmekle aynı sorunu yaşadım. Kullandım ActivityInstrumentationTestCase2ve MockitoAnnotations.initMocks(this);çalışmadım. Bu sorunu sırasıyla alanı olan başka bir sınıfla çözdüm. Örneğin:

class CaptorHolder {

        @Captor
        ArgumentCaptor<Callback<AuthResponse>> captor;

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

Ardından, aktivite testi yönteminde:

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

Mockito'nun GitHub'ında bu sorunla ilgili açık bir sorun var.

Sizi testlerinizde ek açıklamalar kullanmaya zorlamayan basit bir geçici çözüm buldum:

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);
        }

    }

}

Burada olan , @Captorek açıklama ile yeni bir sınıf oluşturmak ve onu esir enjekte etmek. Sonra biz sadece esir ayıklamak ve statik yöntemimizi iade.

Testinizde şu şekilde kullanabilirsiniz:

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

Veya Jackson'a benzeyen sözdizimi ile TypeReference:

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

Çalışır, çünkü Mockito aslında herhangi bir tür bilgiye ihtiyaç duymaz (örneğin serileştiricilerin aksine).

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.