411 Stimmen

So erfassen Sie eine Liste eines bestimmten Typs mit Mockito

Gibt es eine Möglichkeit, eine Liste eines bestimmten Typs mit mockitos ArgumentCaptore zu erfassen. Dies funktioniert nicht:

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

10 Stimmen

Ich finde, dass es eine schreckliche Idee ist, hier eine konkrete Listenimplementierung zu verwenden ( ArrayList ). Sie können jederzeit die List Schnittstelle, und wenn Sie die Tatsache darstellen wollen, dass sie kovariant ist, dann können Sie extends : ArgumentCaptor<? extends List<SomeType>>

685voto

crunchdog Punkte 12308

Das verschachtelte Generika-Problem kann mit dem @Captor-Anmerkung :

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

73 Stimmen

Ich bevorzuge die Verwendung von MockitoAnnotations.initMocks(this) im @Before Methode, anstatt einen Läufer zu verwenden, der die Möglichkeit ausschließt, einen anderen Läufer zu verwenden. Aber +1, danke für den Hinweis auf die Anmerkung.

5 Stimmen

Ich bin nicht sicher, ob dieses Beispiel vollständig ist. Ich erhalte... Error:(240, 40) java: variable captor might not have been initialized ich mag tenshi's Antwort unten

1 Stimmen

Ich hatte das gleiche Problem und fand diesen Blogbeitrag, der mir ein wenig geholfen hat: blog.jdriven.com/2012/10/ . Es enthält einen Schritt zur Verwendung von MockitoAnnotations.initMocks, nachdem Sie die Annotation in Ihrer Klasse platziert haben. Eine Sache, die mir aufgefallen ist, ist, dass man sie nicht innerhalb einer lokalen Variable haben kann.

183voto

Paŭlo Ebermann Punkte 70779

Ja, dies ist ein allgemeines generisches Problem, nicht mockito-spezifisch.

Es gibt kein Klassenobjekt für ArrayList<SomeType> und daher kann man ein solches Objekt nicht typsicher an eine Methode übergeben, die eine Class<ArrayList<SomeType>> .

Sie können das Objekt in den richtigen Typ umwandeln:

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

Dies wird einige Warnungen über unsichere Casts ausgeben, und natürlich kann Ihr ArgumentCaptor nicht wirklich unterscheiden zwischen ArrayList<SomeType> y ArrayList<AnotherType> ohne vielleicht die Elemente zu inspizieren.

(Wie in der anderen Antwort erwähnt, ist dies zwar ein allgemeines Generika-Problem, aber es gibt eine Mockito-spezifische Lösung für das Problem der Typsicherheit mit der @Captor Bemerkung. Es kann immer noch nicht unterscheiden zwischen einer ArrayList<SomeType> und ein ArrayList<OtherType> .)

Edita:

Werfen Sie auch einen Blick auf tenshi Kommentar. Sie können den ursprünglichen Code durch diese vereinfachte Version ersetzen:

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

63 Stimmen

Das von Ihnen gezeigte Beispiel kann vereinfacht werden, da Java für die statischen Methodenaufrufe eine Typinferenz vornimmt: ArgumentCaptor<List<SimeType>> argument = ArgumentCaptor.forClass((Class) List.class);

6 Stimmen

Zum Deaktivieren der unkontrollierte oder unsichere Operationen verwendet Warnung, verwenden Sie die @SuppressWarnings("unchecked") oberhalb der Zeile für die Definition des Argument-Captors. Auch das Casting nach Class ist überflüssig.

3 Stimmen

Das Casting für Class ist in meinen Tests nicht redundant.

26voto

rogerdpack Punkte 55995

Wenn Sie keine Angst vor alten Java-Stil (nicht Typ sicher generische) Semantik, dies funktioniert auch und ist simple'ish:

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

4 Stimmen

Sie können @SuppressWarnings("rawtypes") vor der Deklaration hinzufügen, um Warnungen zu deaktivieren.

9voto

kkmike999 Punkte 101
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"));

9voto

mrts Punkte 13405

Basierend auf den Kommentaren von @tenshi und @pkalinow (auch ein Lob an @rogerdpack), ist die folgende einfache Lösung für die Erstellung eines Listenargument-Captors, der auch die "verwendet ungeprüfte oder unsichere Operationen" Warnung:

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

Vollständiges Beispiel ici und den entsprechenden erfolgreichen CI-Build und Testlauf ici .

Unser Team verwendet dies schon seit einiger Zeit in unseren Unit-Tests, und es scheint die einfachste Lösung für uns zu sein.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X