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();
    }
}