Использование Mockito для мокирования некоторых методов, но не всех
Существует ли способ, используя Mockito, замокировать некоторые методы в классе, но не замокировать другие?
Например, в этом (хотя и искусственном) классе Stock
я хочу замокировать возвращаемые значения методов getPrice()
и getQuantity()
(как показано в приведенном ниже фрагменте теста), но при этом хочу, чтобы метод getValue()
выполнял умножение, как это прописано в самом классе Stock
.
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();
// К сожалению, следующий assert не проходит, так как замокированный метод getValue() не выполняет код вычисления из Stock.getValue().
assertEquals("Стоимость акций неверна", 100.00*200, value, .00001);
}
2 ответ(ов)
Вы можете использовать org.mockito.Mockito.CALLS_REAL_METHODS
, как указано в документации:
/**
* Опциональный <code>Answer</code>, который можно использовать с {@link Mockito#mock(Class, Answer)}
* <p>
* {@link Answer} может быть использован для определения значений, возвращаемых не заглушенными вызовами.
* <p>
* Эта реализация может быть полезна при работе с устаревшим кодом.
* Когда используется эта реализация, не заглушенные методы будут делегировать вызовы на реальную реализацию.
* Это способ создать частичную заглушку, которая по умолчанию вызывает реальные методы.
* <p>
* Как обычно, вы должны прочитать <b>предупреждение о частичной заглушке</b>:
* Объектно-ориентированное программирование в большей степени направлено на решение сложности путем деления на отдельные, специфические объекты, соблюдающие принцип единственной ответственности (SRP).
* Как частичная заглушка вписывается в эту парадигму? Фактически, она не вписывается...
* Частичная заглушка обычно означает, что сложность была перенесена в другой метод того же объекта.
* В большинстве случаев это не лучший способ проектирования вашего приложения.
* <p>
* Тем не менее, есть редкие случаи, когда частичные заглушки могут оказаться полезными:
* если у вас есть код, который нельзя легко изменить (интерфейсы сторонних разработчиков, промежуточная рефакторификация устаревшего кода и т. д.).
* Однако я бы не стал использовать частичные заглушки для нового, управляемого тестами и хорошо спроектированного кода.
* <p>
* Пример:
* <pre class="code"><code class="java">
* Foo mock = mock(Foo.class, CALLS_REAL_METHODS);
*
* // это вызывает реальную реализацию Foo.getSomething()
* value = mock.getSomething();
*
* when(mock.getSomething()).thenReturn(fakeValue);
*
* // теперь возвращается fakeValue
* value = mock.getSomething();
* </code></pre>
*/
Таким образом, ваш код должен выглядеть следующим образом:
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("Значение акции неверно", 100.00 * 200, value, .00001);
}
}
Вызов Stock stock = mock(Stock.class);
вызывает org.mockito.Mockito.mock(Class<T>)
, который выглядит следующим образом:
public static <T> T mock(Class<T> classToMock) {
return mock(classToMock, withSettings().defaultAnswer(RETURNS_DEFAULTS));
}
Документация по значению RETURNS_DEFAULTS
гласит:
/**
* Значение по умолчанию <code>Answer</code> для каждой заглушки <b>если</b> заглушка не была заглушена.
* Обычно она просто возвращает какое-то пустое значение.
* <p>
* {@link Answer} может быть использован для определения значений, возвращаемых не заглушенными вызовами.
* <p>
* Эта реализация сначала пытается использовать глобальную конфигурацию.
* Если глобальной конфигурации нет, тогда используется {@link ReturnsEmptyValues} (возвращает нули, пустые коллекции, нули и т. д.)
*/
Частичное мокирование с использованием метода spy библиотеки Mockito может быть решением вашей проблемы, как уже упоминалось в предыдущих ответах. В какой-то степени я согласен с тем, что для вашего конкретного случая может быть более уместно замокировать обращение к базе данных. Однако, исходя из моего опыта, это не всегда возможно - по крайней мере, не без других обходных путей, которые я бы считал довольно громоздкими или хотя бы хрупкими. Обратите внимание, что частичное мокирование работает не со всеми версиями Mockito. Вам потребуется как минимум версия 1.8.0.
Я бы просто написал краткий комментарий к исходному вопросу, а не публиковал бы этот ответ, но StackOverflow этого не позволяет.
Еще один момент: мне совершенно непонятно, почему многие вопросы здесь сопровождаются комментариями в духе "Зачем вы хотите это сделать?", не пытаясь хотя бы понять суть проблемы. Особенно когда речь идет о необходимости частичного мокирования, я могу представить множество сценариев, где это было бы полезно. Именно поэтому разработчики Mockito реализовали эту функциональность. Конечно, эту возможность не следует использовать чрезмерно. Но когда мы говорим о настройках тестов, которые в противном случае было бы очень сложно установить, использование spy просто необходимо.
Как замокировать методы с возвращаемым типом void с помощью Mockito
Mockito: Тестирование void метода, который выбрасывает исключение
"Как сделать так, чтобы имитируемый метод возвращал аргумент, который был ему передан?"
Как проверить, что конкретный метод не был вызван, используя Mockito?
Как создать утечку памяти в Java?