496 Stimmen

Mockito: Der Versuch, eine Methode auszuspionieren, ruft die ursprüngliche Methode auf

Ich verwende Mockito 1.9.0. Ich möchte das Verhalten einer einzelnen Methode einer Klasse in einem JUnit-Test nachbilden, also habe ich

final MyClass myClassSpy = Mockito.spy(myInstance);
Mockito.when(myClassSpy.method1()).thenReturn(myResults);

Das Problem ist, dass in der zweiten Zeile, myClassSpy.method1() tatsächlich aufgerufen wird, was zu einer Ausnahme führt. Der einzige Grund, warum ich Mocks verwende, ist, dass ich später, wenn myClassSpy.method1() aufgerufen wird, wird die eigentliche Methode nicht aufgerufen und die myResults Objekt zurückgegeben werden.

MyClass ist eine Schnittstelle und myInstance ist eine Implementierung davon, falls das wichtig ist.

Was muss ich tun, um dieses Spionageverhalten zu korrigieren?

834voto

Tomasz Nurkiewicz Punkte 322861

Darf ich zitieren die offizielle Dokumentation :

Wichtiger Hinweis zum Ausspähen von realen Objekten!

Manchmal ist es unmöglich, when(Object) für Stubbing-Spione zu verwenden. Beispiel:

List list = new LinkedList();
List spy = spy(list);

// Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty)
when(spy.get(0)).thenReturn("foo");

// You have to use doReturn() for stubbing
doReturn("foo").when(spy).get(0);

In Ihrem Fall sieht das so aus:

doReturn(resultsIWant).when(myClassSpy).method1();

60voto

ejaenv Punkte 1695

In meinem Fall, bei der Verwendung von Mockito 2.0, musste ich alle any() Parameter zu nullable() um den eigentlichen Anruf zu vereiteln.

30voto

Maragues Punkte 36662

Mein Fall unterschied sich von der üblichen Antwort. Ich habe versucht, eine paketprivate Methode für eine Instanz nachzubilden, die nicht in diesem Paket enthalten ist

package common;

public class Animal {
  void packageProtected();
}

package instances;

class Dog extends Animal { }

und die Testklassen

package common;

public abstract class AnimalTest<T extends Animal> {
  @Before
  setup(){
    doNothing().when(getInstance()).packageProtected();
  }

  abstract T getInstance();
}

package instances;

class DogTest extends AnimalTest<Dog> {
  Dog getInstance(){
    return spy(new Dog());
  }

  @Test
  public void myTest(){}
}

Die Kompilierung ist korrekt, aber wenn sie versucht, den Test einzurichten, ruft sie stattdessen die echte Methode auf.

Deklaration der Methode geschützt o öffentlich behebt das Problem, auch wenn es keine saubere Lösung ist.

22voto

mike rodent Punkte 12041

Die Antwort von Tomasz Nurkiewicz scheint nicht die ganze Geschichte zu erzählen!

NB Mockito Version: 1.10.19.

Ich bin ein absoluter Mockito-Neuling und kann mir das folgende Verhalten nicht erklären. Wenn es einen Experten gibt, der diese Antwort verbessern kann, bitte ich um Verständnis.

Die Methode, um die es hier geht, getContentStringValue ist NICHT final y NICHT static .

Diese Linie hace die ursprüngliche Methode aufrufen getContentStringValue :

doReturn( "dummy" ).when( im ).getContentStringValue( anyInt(), isA( ScoreDoc.class ));

Diese Linie nicht die ursprüngliche Methode aufrufen getContentStringValue :

doReturn( "dummy" ).when( im ).getContentStringValue( anyInt(), any( ScoreDoc.class ));

Aus Gründen, die ich nicht beantworten kann, ist die Verwendung von isA() bewirkt das beabsichtigte (?) "Methode nicht aufrufen"-Verhalten von doReturn zu scheitern.

Schauen wir uns die beteiligten Methodensignaturen an: Sie sind beide static Methoden der Matchers . Beide geben laut Javadoc Folgendes zurück null was an sich schon ein wenig schwierig zu verstehen ist. Vermutlich ist die Class Objekt, das als Parameter übergeben wird, wird untersucht, aber das Ergebnis entweder nie berechnet oder verworfen. Angesichts der Tatsache, dass null für eine beliebige Klasse stehen kann und dass Sie hoffen, dass die verspottete Methode nicht aufgerufen wird, könnten nicht die Signaturen von isA( ... ) y any( ... ) einfache Rückkehr null und nicht ein allgemeiner Parameter* <T> ?

Wie auch immer:

public static <T> T isA(java.lang.Class<T> clazz)

public static <T> T any(java.lang.Class<T> clazz)

In der API-Dokumentation finden sich dazu keine Anhaltspunkte. Sie scheint auch zu sagen, dass die Notwendigkeit für ein solches Verhalten "Methode nicht aufrufen" "sehr selten" ist. Persönlich verwende ich diese Technik die ganze Zeit Typischerweise finde ich, dass Spott ein paar Zeilen beinhaltet, die "die Szene festlegen" ... gefolgt vom Aufruf einer Methode, die dann die Szene in dem von Ihnen inszenierten Scheinkontext "abspielt" ... und während Sie die Szenerie und die Requisiten aufbauen, ist das Letzte, was Sie wollen, dass die Schauspieler die Bühne links betreten und anfangen, ihre Herzen zu spielen ...

Aber das geht weit über meine Gehaltsklasse hinaus... Ich bitte um Erklärungen von allen vorbeikommenden Mockito-Hochpriestern...

* Ist "allgemeiner Parameter" der richtige Begriff?

19voto

Adrian Kapuscinski Punkte 1064

Ein weiteres mögliches Szenario, das zu Problemen mit Spionen führen kann, ist, wenn Sie testen Frühlingsbohnen (mit Spring-Test-Framework) oder eine andere Framework, das Ihre Objekte während des Tests anzeigt .

Exemple

@Autowired
private MonitoringDocumentsRepository repository

void test(){
    repository = Mockito.spy(repository)
    Mockito.doReturn(docs1, docs2)
            .when(repository).findMonitoringDocuments(Mockito.nullable(MonitoringDocumentSearchRequest.class));
}

Im obigen Code versuchen sowohl Spring als auch Mockito, Ihr MonitoringDocumentsRepository-Objekt als Proxy zu verwenden, aber Spring ist zuerst dran, was zu einem echten Aufruf der findMonitoringDocuments-Methode führt. Wenn wir unseren Code debuggen, nachdem wir das Repository-Objekt ausspioniert haben, sieht es im Debugger wie folgt aus:

repository = MonitoringDocumentsRepository$$EnhancerBySpringCGLIB$$MockitoMock$

@SpyBean zur Rettung

Wenn stattdessen @Autowired Vermerk verwenden wir @SpyBean Annotation lösen wir das obige Problem. Die SpyBean-Annotation injiziert auch das Repository-Objekt, aber es wird zunächst von Mockito proxifiziert und sieht im Debugger wie folgt aus

repository = MonitoringDocumentsRepository$$MockitoMock$$EnhancerBySpringCGLIB$

und hier ist der Code:

@SpyBean
private MonitoringDocumentsRepository repository

void test(){
    Mockito.doReturn(docs1, docs2)
            .when(repository).findMonitoringDocuments(Mockito.nullable(MonitoringDocumentSearchRequest.class));
}

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