Mockito Как издеваться над вызовом метода суперкласса
Я использую Mockito в некоторых тестах.
У меня есть следующие классы:
class BaseService {
public void save() {...}
}
public Childservice extends BaseService {
public void save(){
//some code
super.save();
}
}
Я хочу высмеять только второй вызов (super.save
) ChildService
. Первый вызов должен вызвать реальный метод. Есть ли способ сделать это?
Ответы
Ответ 1
Нет, Мокито не поддерживает это.
Это может быть не тот ответ, который вы ищете, но то, что вы видите, является симптомом отказа от применения принципа дизайна:
Наслаждайтесь композицией над наследованием
Если вы выберете стратегию вместо расширения суперкласса, проблема исчезнет.
Если вам не разрешено изменять код, но вы все равно должны его протестировать, и в этом неудобном случае все еще есть надежда. С некоторыми инструментами AOP (например, AspectJ) вы можете переплетать код в метод суперкласса и полностью исключить его выполнение (yuck). Это не работает, если вы используете прокси-серверы, вы должны использовать модификацию байтового кода (время ткачества или компиляцию времени). Есть и фальшивые рамки, которые поддерживают этот тип трюка, например PowerMock и PowerMockito.
Я предлагаю вам пойти на рефакторинг, но если это не вариант, вам нужно какое-то серьезное удовольствие от взлома.
Ответ 2
Если у вас действительно нет выбора для рефакторинга, вы можете издеваться/заглушить все в вызове супер метода, например.
class BaseService {
public void validate(){
fail(" I must not be called");
}
public void save(){
//Save method of super will still be called.
validate();
}
}
class ChildService extends BaseService{
public void load(){}
public void save(){
super.save();
load();
}
}
@Test
public void testSave() {
ChildService spy = Mockito.spy(new ChildService());
// Prevent/stub logic in super.save()
Mockito.doNothing().when((BaseService)spy).validate();
// When
spy.save();
// Then
verify(spy).load();
}
Ответ 3
Рассмотрим рефакторинг кода из метода ChildService.save() для другого метода и протестируйте этот новый метод вместо тестирования ChildService.save(), таким образом вы избежите ненужного вызова метода super.
Пример:
class BaseService {
public void save() {...}
}
public Childservice extends BaseService {
public void save(){
newMethod();
super.save();
}
public void newMethod(){
//some codes
}
}
Ответ 4
создать защищенный пакет (предполагает метод тестирования в том же пакете) в подклассе, который вызывает метод суперкласса, а затем вызывать этот метод в методе переопределенного подкласса. вы можете установить ожидания этого метода в своем тесте с помощью шаблона шпиона. не очень, но, конечно, лучше, чем иметь дело со всеми настройками ожидания для супер метода в вашем тесте
Ответ 5
Причина в том, что ваш базовый класс не является общедоступным, тогда Mockito не может перехватить его из-за видимости, если вы измените базовый класс как открытый или @Override в подклассе (как открытый), то Mockito может правильно его издеваться.
public class BaseService{
public boolean foo(){
return true;
}
}
public ChildService extends BaseService{
}
@Test
@Mock ChildService childService;
public void testSave() {
Mockito.when(childService.foo()).thenReturn(false);
// When
assertFalse(childService.foo());
}
Ответ 6
Может быть, самый простой вариант, если наследование имеет смысл, - создать новый метод (частный пакет), чтобы вызвать супер (позволяет называть его superFindall), шпионить реальный экземпляр, а затем издеваться над методом superFindAll() так, как вы хотел высмеять родительский класс. Это не идеальное решение с точки зрения охвата и видимости, но оно должно выполнять свою работу и легко применять.
public Childservice extends BaseService {
public void save(){
//some code
superSave();
}
void superSave(){
super.save();
}
}