web-dev-qa-db-de.com

Verwenden Sie Mockito, um einige Methoden zu verspotten, andere jedoch nicht

Gibt es eine Möglichkeit, mit Mockito einige Methoden in einer Klasse zu verspotten, andere jedoch nicht?

In dieser (zugegebenermaßen konstruierten) Stock-Klasse möchte ich beispielsweise die Rückgabewerte getPrice () und getQuantity () verspotten (wie im Testschnipsel unten gezeigt), aber ich möchte, dass getValue () die Multiplikation wie im Stock codiert durchführt Klasse

public class Stock {
  private final double price;
  private final int quantity;

  Stock(double price, int quantity) {
    this.price = price;
    this.quantity = quantity;
  }

  public double getPrice() {
    return price;
  }

  public int getQuantity() {
    return quantity;
  }
  public double getValue() {
    return getPrice() * getQuantity();
  }

  @Test
  public void getValueTest() {
    Stock stock = mock(Stock.class);
    when(stock.getPrice()).thenReturn(100.00);
    when(stock.getQuantity()).thenReturn(200);
    double value = stock.getValue();
    // Unfortunately the following assert fails, because the mock Stock getValue() method does not perform the Stock.getValue() calculation code.
    assertEquals("Stock value not correct", 100.00*200, value, .00001);
}
289
Victor Grazi

Um Ihre Frage direkt zu beantworten, können Sie einige Methoden verspotten, ohne andere zu verspotten. Dies wird als partieller Schein bezeichnet. Siehe die Mockito-Dokumentation zu Teilmodellen für weitere Informationen.

Für Ihr Beispiel können Sie in Ihrem Test Folgendes tun:

Stock stock = mock(Stock.class);
when(stock.getPrice()).thenReturn(100.00);    // Mock implementation
when(stock.getQuantity()).thenReturn(200);    // Mock implementation
when(stock.getValue()).thenCallRealMethod();  // Real implementation

In diesem Fall wird jede Methodenimplementierung verspottet, es sei denn, Sie geben thenCallRealMethod() in der when(..)-Klausel an.

Es gibt auch eine andere Möglichkeit mit spy anstelle von mock:

Stock stock = spy(Stock.class);
when(stock.getPrice()).thenReturn(100.00);    // Mock implementation
when(stock.getQuantity()).thenReturn(200);    // Mock implementation
// All other method call will use the real implementations

In diesem Fall sind alle Methoden die eigentliche Implementierung, es sei denn, Sie haben mit when(..) ein simuliertes Verhalten definiert.

Es gibt eine wichtige Gefahr, wenn Sie when(Object) mit Spion wie im vorherigen Beispiel verwenden. Die echte Methode wird aufgerufen (weil stock.getPrice() zur Laufzeit vor when(..) ausgewertet wird). Dies kann ein Problem sein, wenn Ihre Methode eine Logik enthält, die nicht aufgerufen werden sollte. Sie können das vorherige Beispiel so schreiben:

Stock stock = spy(Stock.class);
doReturn(100.00).when(stock).getPrice();    // Mock implementation
doReturn(200).when(stock).getQuantity();    // Mock implementation
// All other method call will use the real implementations

Ich glaube jedoch, dass Ihr Beispiel mit Ihrem Beispiel immer noch fehlschlagen wird, da die Implementierung von getValue() auf quantity und price und nicht auf getQuantity() und getPrice() beruht, was Sie verspottet haben.

Was Sie wirklich zu haben scheinen, ist nur:

@Test
public void getValueTest() {
    Stock stock = new Stock(100.00, 200);
    double value = stock.getValue();
    assertEquals("Stock value not correct", 100.00*200, value, .00001);
}
465
Jon Newmuis

Partielle Verspottungen einer Klasse werden auch über Spy in mockito unterstützt

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

//optionally, you can stub out some methods:
when(spy.size()).thenReturn(100);

//using the spy calls real methods
spy.add("one");
spy.add("two");

//size() method was stubbed - 100 is printed
System.out.println(spy.size());

Überprüfen Sie die 1.10.19 und 2.7.22 docs für eine detaillierte Erklärung.

119
Sudarshan

Nach docs :

Foo mock = mock(Foo.class, CALLS_REAL_METHODS);

// this calls the real implementation of Foo.getSomething()
value = mock.getSomething();

when(mock.getSomething()).thenReturn(fakeValue);

// now fakeValue is returned
value = mock.getSomething();
21
ema

Die akzeptierte Antwort ist gemäß der Frage nicht korrekt.

Der Aufruf von Stock stock = mock(Stock.class); ruft org.mockito.Mockito.mock(Class<T>) auf, die wie folgt aussieht:

 public static <T> T mock(Class<T> classToMock) {
    return mock(classToMock, withSettings().defaultAnswer(RETURNS_DEFAULTS));
}

Die Dokumente des Wertes RETURNS_DEFAULTS sagen:

/**
 * The default <code>Answer</code> of every mock <b>if</b> the mock was not stubbed.
 * Typically it just returns some empty value. 
 * <p>
 * {@link Answer} can be used to define the return values of unstubbed invocations. 
 * <p>
 * This implementation first tries the global configuration. 
 * If there is no global configuration then it uses {@link ReturnsEmptyValues} (returns zeros, empty collections, nulls, etc.)
 */

Was Sie wollen, ist org.mockito.Mockito.CALLS_REAL_METHODS gemäß den Dokumenten:

/**
 * Optional <code>Answer</code> to be used with {@link Mockito#mock(Class, Answer)}
 * <p>
 * {@link Answer} can be used to define the return values of unstubbed invocations.
 * <p>
 * This implementation can be helpful when working with legacy code.
 * When this implementation is used, unstubbed methods will delegate to the real implementation.
 * This is a way to create a partial mock object that calls real methods by default.
 * <p>
 * As usual you are going to read <b>the partial mock warning</b>:
 * Object oriented programming is more less tackling complexity by dividing the complexity into separate, specific, SRPy objects.
 * How does partial mock fit into this paradigm? Well, it just doesn't... 
 * Partial mock usually means that the complexity has been moved to a different method on the same object.
 * In most cases, this is not the way you want to design your application.
 * <p>
 * However, there are rare cases when partial mocks come handy: 
 * dealing with code you cannot change easily (3rd party interfaces, interim refactoring of legacy code etc.)
 * However, I wouldn't use partial mocks for new, test-driven & well-designed code.
 * <p>
 * Example:
 * <pre class="code"><code class="Java">
 * Foo mock = mock(Foo.class, CALLS_REAL_METHODS);
 *
 * // this calls the real implementation of Foo.getSomething()
 * value = mock.getSomething();
 *
 * when(mock.getSomething()).thenReturn(fakeValue);
 *
 * // now fakeValue is returned
 * value = mock.getSomething();
 * </code></pre>
 */

So sollte Ihr Code aussehen:

import org.junit.Test;
import static org.mockito.Mockito.*;
import static org.junit.Assert.*;

public class StockTest {

    public class Stock {
        private final double price;
        private final int quantity;

        Stock(double price, int quantity) {
            this.price = price;
            this.quantity = quantity;
        }

        public double getPrice() {
            return price;
        }

        public int getQuantity() {
            return quantity;
        }

        public double getValue() {
            return getPrice() * getQuantity();
        }
    }

    @Test
    public void getValueTest() {
        Stock stock = mock(Stock.class, withSettings().defaultAnswer(CALLS_REAL_METHODS));
        when(stock.getPrice()).thenReturn(100.00);
        when(stock.getQuantity()).thenReturn(200);
        double value = stock.getValue();

        assertEquals("Stock value not correct", 100.00 * 200, value, .00001);
    }
}
15
the dude

Ein partielles Spott mit der Spionagemethode von Mockito könnte die Lösung für Ihr Problem sein, wie bereits in den Antworten oben angegeben. Bis zu einem gewissen Grad stimme ich zu, dass es für Ihren konkreten Anwendungsfall angemessener ist, die DB-Suche zu simulieren. Aus meiner Erfahrung ist dies nicht immer möglich - zumindest nicht ohne andere Problemumgehungen -, die ich als sehr umständlich oder zumindest fragil empfinden würde. Beachten Sie, dass partielles Spott nicht mit verbündeten Versionen von Mockito funktioniert. Sie haben mindestens 1.8.0 verwendet.

Ich hätte nur einen einfachen Kommentar für die ursprüngliche Frage geschrieben, anstatt diese Antwort zu posten, aber StackOverflow lässt dies nicht zu.

Nur eine weitere Sache: Ich kann wirklich nicht verstehen, dass oft eine Frage gestellt wird, die mit "Warum Sie das tun wollen" kommentiert wird, ohne zumindest das Problem zu verstehen. Gerade wenn es dann zu partiellem Spott kommt, gibt es wirklich viele Anwendungsfälle, die ich mir vorstellen könnte, wo es sinnvoll wäre. Deshalb haben die Jungs von Mockito diese Funktionalität bereitgestellt. Diese Funktion sollte natürlich nicht überbeansprucht werden. Wenn wir jedoch über Testfall-Setups sprechen, die sonst nicht sehr kompliziert aufgebaut werden könnten, sollte Spionage eingesetzt werden. 

0
kflGalore