Verwenden von Mockito zum Nachahmen von Klassen mit generischen Parametern

Lesezeit: 6 Minuten

Gibt es eine saubere Methode, eine Klasse mit generischen Parametern zu verspotten? Angenommen, ich muss eine Klasse verspotten Foo<T> die ich in eine Methode übergeben muss, die a erwartet Foo<Bar>. Folgendes kann ich problemlos tun:

Foo mockFoo = mock(Foo.class);
when(mockFoo.getValue).thenReturn(new Bar());

Vorausgesetzt getValue() gibt den generischen Typ zurück T. Aber das wird Kätzchen haben, wenn ich es später in eine Methode erwarte Foo<Bar>. Ist Casting die einzige Möglichkeit, dies zu tun?

  • Warum foo und bar gegenüber aussagekräftigeren Namen verwenden? Hat gerade bei vielen Leuten eine ganze Menge Verwirrung gestiftet.

    – Kaigo

    25. Juli 2020 um 20:26 Uhr

  • @Kaigo Es ist ziemlich üblich, in Programmierbeispielen foo, bar und baz zu verwenden, insbesondere wenn Beispiele aus der realen Welt anonymisiert werden, um vertrauliche Details zu verbergen. Jetzt mache ich mir mehr Sorgen um die Tatsache, dass OP den Ausdruck “das wird Kätzchen haben” verwendet hat … buchstäblich noch nie zuvor jemanden sagen hören;)

    – Adam Burley

    13. März 2021 um 14:04 Uhr

  • Jeder weiß, dass “Kätzchen zu haben” eine schlechte Sache für Code ist, auch wenn es für Katzen ziemlich großartig ist. :--)

    – Charlie Reitzel

    29. Januar um 18:12 Uhr

Benutzer-Avatar
Johannes Paulett

Ich denke, Sie müssen es werfen, aber es sollte nicht so schlimm sein:

Foo<Bar> mockFoo = (Foo<Bar>) mock(Foo.class);
when(mockFoo.getValue()).thenReturn(new Bar());

  • Ja, aber Sie haben immer noch eine Warnung. Kann man die Abmahnung umgehen?

    – odwl

    31. August 2010 um 19:11 Uhr

  • @SuppressWarnings(“nicht markiert”)

    – qualifiziert

    10. Dezember 2010 um 1:13 Uhr

  • Ich denke, das ist völlig akzeptabel, da wir über ein Scheinobjekt in einem Komponententest sprechen.

    – Magnilex

    26. November 2014 um 13:06 Uhr

  • @demaniak Es funktioniert überhaupt nicht. Argument-Matcher können in diesem Kontext nicht verwendet werden.

    – Krzysztof Krason

    19. Mai 2018 um 20:53 Uhr


  • @demaniak Das lässt sich gut kompilieren, aber wenn der Test ausgeführt wird, wird InvalidUseOfMatchersException ausgelöst (was eine RuntimeException ist).

    – Superole

    4. Juni 2018 um 12:32 Uhr

Benutzer-Avatar
Marek Kirejczyk

Eine andere Möglichkeit, dies zu umgehen, ist die Verwendung @Mock Anmerkung statt. Funktioniert nicht in allen Fällen, sieht aber viel sexier aus 🙂

Hier ist ein Beispiel:

@RunWith(MockitoJUnitRunner.class)
public class FooTests {

    @Mock
    public Foo<Bar> fooMock;
    
    @Test
    public void testFoo() {
        when(fooMock.getValue()).thenReturn(new Bar());
    }
}

Das MockitoJUnitRunner initialisiert die mit annotierten Felder @Mock.

  • dies ist in 1.9.5 veraltet. 🙁 Scheint mir viel sauberer zu sein.

    – Pure Funktion

    27. März 2014 um 21:36 Uhr

  • @CodeNovitiate Ich konnte in 1.9.5 keine Verfallsanmerkungen zu MockitoJUnitRunner und Mock finden. Also, was ist veraltet? (Ja, org.mockito.MockitoAnnotations.Mock ist veraltet, aber Sie sollten stattdessen org.mockito.Mock verwenden)

    – neu242

    22. Mai 2014 um 7:31 Uhr

  • Gut gemacht, das hat bei mir perfekt funktioniert. Es ist nicht nur “sexier”, es vermeidet eine Warnung ohne Verwendung SuppressWarnings. Warnungen gibt es aus einem bestimmten Grund, es ist besser, sie nicht zu unterdrücken. Vielen Dank!

    – Nicole

    3. Juni 2014 um 17:43 Uhr

  • Es gibt eine Sache, die ich an der Verwendung nicht mag @Mock Anstatt von mock(): Die Felder sind während der Bauzeit immer noch null, daher kann ich zu diesem Zeitpunkt keine Abhängigkeiten einfügen und die Felder nicht endgültig machen. Ersteres kann durch a gelöst werden @Before-kommentierte Methode natürlich.

    – Rüdiger Schulz

    25. September 2014 um 9:26 Uhr


  • Rufen Sie zur Initiierung einfach MockitoAnnotations.initMocks(this) auf;

    – borjab

    6. April 2016 um 12:31 Uhr

Benutzer-Avatar
Singleton

Sie könnten immer eine Zwischenklasse/Schnittstelle erstellen, die den generischen Typ erfüllt, den Sie angeben möchten. Wenn beispielsweise Foo eine Schnittstelle wäre, könnten Sie die folgende Schnittstelle in Ihrer Testklasse erstellen.

private interface FooBar extends Foo<Bar>
{
}

In Situationen, in denen Foo a nicht endgültig Klasse, Sie könnten die Klasse einfach mit dem folgenden Code erweitern und dasselbe tun:

public class FooBar extends Foo<Bar>
{
}

Dann könnten Sie eines der obigen Beispiele mit dem folgenden Code verwenden:

Foo<Bar> mockFoo = mock(FooBar.class);
when(mockFoo.getValue()).thenReturn(new Bar());

  • Bereitgestellt Foo eine Schnittstelle oder nicht-finale Klasse ist, scheint dies eine recht elegante Lösung zu sein. Vielen Dank.

    – Tim Clemons

    29. April 2014 um 15:39 Uhr

  • Ich habe die Antwort aktualisiert, um auch Beispiele für nicht endgültige Klassen aufzunehmen. Idealerweise würden Sie gegen eine Schnittstelle codieren, aber das wird nicht immer der Fall sein. Guter Fang!

    – Singleton

    30. April 2014 um 15:15 Uhr

Ein … kreieren Test-Utility-Methode. Besonders nützlich, wenn Sie es mehr als einmal benötigen.

@Test
public void testMyTest() {
    // ...
    Foo<Bar> mockFooBar = mockFoo();
    when(mockFooBar.getValue).thenReturn(new Bar());

    Foo<Baz> mockFooBaz = mockFoo();
    when(mockFooBaz.getValue).thenReturn(new Baz());

    Foo<Qux> mockFooQux = mockFoo();
    when(mockFooQux.getValue).thenReturn(new Qux());
    // ...
}

@SuppressWarnings("unchecked") // still needed :( but just once :)
private <T> Foo<T> mockFoo() {
    return mock(Foo.class);
}

Ich stimme zu, dass man Warnungen in Klassen oder Methoden nicht unterdrücken sollte, da man andere, versehentlich unterdrückte Warnungen übersehen könnte. Aber IMHO ist es absolut sinnvoll, eine Warnung zu unterdrücken, die nur eine einzige Codezeile betrifft.

@SuppressWarnings("unchecked")
Foo<Bar> mockFoo = mock(Foo.class);

  • Tut mir leid, dass ich diesen Beitrag necro, aber ich habe noch nie Anmerkungen zu einer Aussage wie dieser in Java gesehen, und die Dokumente sag auch nichts dazu. Übersehe ich etwas?

    – Rabadash8820

    12. August um 16:30 Uhr

Wie in den anderen Antworten erwähnt, gibt es keine gute Möglichkeit, die zu verwenden mock() & spy() Methoden direkt ohne unsicheren Generika-Zugriff und/oder Unterdrückung von Generika-Warnungen.

Es gibt derzeit ein offenes Problem im Mockito-Projekt (#1531), um Unterstützung für die Verwendung von hinzuzufügen mock() & spy() Methoden ohne Generika-Warnungen. Das Problem wurde im November 2018 eröffnet, aber es gibt keine Hinweise darauf, dass es priorisiert wird. Laut einem der Kommentare des Mockito-Mitarbeiters zu diesem Problem:

Angesichts dessen .class nicht gut mit Generika spielt, glaube ich nicht, dass wir in Mockito eine Lösung finden können. Kannst du schon @Mock (Die JUnit5-Erweiterung erlaubt auch Methodenparameter @Mocks) und das sollte eine geeignete Alternative sein. Daher können wir dieses Problem offen halten, aber angesichts dessen ist es unwahrscheinlich, dass es jemals behoben wird @Mock ist eine bessere API.

  • Tut mir leid, dass ich diesen Beitrag necro, aber ich habe noch nie Anmerkungen zu einer Aussage wie dieser in Java gesehen, und die Dokumente sag auch nichts dazu. Übersehe ich etwas?

    – Rabadash8820

    12. August um 16:30 Uhr

Benutzer-Avatar
Vogella

Mit JUnit5 denke ich, dass der beste Weg ist, @ExtendWith(MockitoExtension.class) mit @Mock im Methodenparameter oder im Feld zu verwenden.

Das folgende Beispiel zeigt dies mit Hamcrest-Matchern.

package com.vogella.junit5;                                                                    
                                                                                               
import static org.hamcrest.MatcherAssert.assertThat;                                           
import static org.hamcrest.Matchers.hasItem;                                                   
import static org.mockito.Mockito.verify;                                                      
                                                                                               
import java.util.Arrays;                                                                       
import java.util.List;                                                                         
                                                                                               
import org.junit.jupiter.api.Test;                                                             
import org.junit.jupiter.api.extension.ExtendWith;                                             
import org.mockito.ArgumentCaptor;                                                             
import org.mockito.Captor;                                                                     
import org.mockito.Mock;                                                                       
import org.mockito.junit.jupiter.MockitoExtension;                                             
                                                                                               
@ExtendWith(MockitoExtension.class)                                                            
public class MockitoArgumentCaptureTest {                                                      
                                                                                               
                                                                                               
    @Captor                                                                                    
    private ArgumentCaptor<List<String>> captor;                                               
                                                                                               
    @Test                                                                                      
    public final void shouldContainCertainListItem(@Mock List<String> mockedList) {            
        var asList = Arrays.asList("someElement_test", "someElement");                         
        mockedList.addAll(asList);                                                             
                                                                                               
        verify(mockedList).addAll(captor.capture());                                           
        List<String> capturedArgument = captor.getValue();                                     
        assertThat(capturedArgument, hasItem("someElement"));                                  
    }                                                                                          
}                                                                                              
                                                                                              

Sehen https://www.vogella.com/tutorials/Mockito/article.html für die erforderlichen Maven/Gradle-Abhängigkeiten.

1360110cookie-checkVerwenden von Mockito zum Nachahmen von Klassen mit generischen Parametern

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy