Mockito Как издеваться только над вызовом метода суперкласса - PullRequest
80 голосов
/ 12 августа 2010

Я использую Mockito в некоторых тестах.

У меня есть следующие классы:

class BaseService {  
    public void save() {...}  
}

public Childservice extends BaseService {  
    public void save(){  
        //some code  
        super.save();
    }  
}   

Я хочу высмеивать только второй вызов (super.save) из ChildService Первый вызов должен вызвать реальный метод. Есть ли способ сделать это?

Ответы [ 7 ]

76 голосов
/ 01 октября 2010

Если у вас действительно нет выбора для рефакторинга, вы можете смоделировать / заглушить все в вызове метода super, например,

    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 classToTest = Mockito.spy(new ChildService());

        // Prevent/stub logic in super.save()
        Mockito.doNothing().when((BaseService)classToTest).validate();

        // When
        classToTest.save();

        // Then
        verify(classToTest).load();
    }
53 голосов
/ 13 августа 2010

Нет, Mockito не поддерживает это.

Возможно, это не тот ответ, который вы ищете, но то, что вы видите, является признаком неприменения принципа дизайна:

Пользуется композицией, а не наследованием

Если вы извлекаете стратегию вместо расширения суперкласса, проблема исчезает.

Если вы этого не сделаетеразрешено изменять код, но вы все равно должны его протестировать, и в этом неуклюжем виде еще есть надежда.С помощью некоторых инструментов AOP (например, AspectJ) вы можете встроить код в метод суперкласса и полностью избежать его выполнения (хм).Это не работает, если вы используете прокси, вы должны использовать модификацию байт-кода (или время загрузки, или время компиляции).Существуют фреймворки, поддерживающие этот тип трюков, такие как PowerMock и PowerMockito.

Я предлагаю вам заняться рефакторингом, но если это не вариант, вас ждет серьезное хакерское удовольствие.

4 голосов
/ 18 мая 2013

Рассмотрите возможность рефакторинга кода из метода 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
    }
} 
1 голос
/ 23 мая 2012

создайте защищенный пакет (предполагается, что тестовый класс в том же пакете) в подклассе, который вызывает метод суперкласса, а затем вызовите этот метод в переопределенном методе подкласса. Затем вы можете установить ожидания для этого метода в своем тесте с помощью шаблона шпиона. не красиво, но определенно лучше, чем иметь дело со всеми настройками ожидания для метода super в вашем тесте

0 голосов
/ 04 апреля 2018

Даже если я полностью согласен с ответом iwein (

отдать предпочтение композиции перед наследованием

), я признаю, что иногда наследование кажется просто естественным, и я не чувствую, что его нарушают или реорганизуют просто ради юнит-теста.

Итак, мое предложение:

/**
 * BaseService is now an asbtract class encapsulating 
 * some common logic callable by child implementations
 */
abstract class BaseService {  
    protected void commonSave() {
        // Put your common work here
    }

    abstract void save();
}

public ChildService extends BaseService {  
    public void save() {
        // Put your child specific work here
        // ...

        this.commonSave();
    }  
}

А затем в модульном тесте:

    ChildService childSrv = Mockito.mock(ChildService.class, Mockito.CALLS_REAL_METHODS);

    Mockito.doAnswer(new Answer<Void>() {
        @Override
        public Boolean answer(InvocationOnMock invocation)
                throws Throwable {
            // Put your mocked behavior of BaseService.commonSave() here
            return null;
        }
    }).when(childSrv).commonSave();

    childSrv.save();

    Mockito.verify(childSrv, Mockito.times(1)).commonSave();

    // Put any other assertions to check child specific work is done
0 голосов
/ 10 мая 2017

Возможно, самый простой вариант, если наследование имеет смысл, - это создать новый метод (пакет private ??) для вызова super (давайте назовем его superFindall), шпионить за реальным экземпляром и затем высмеивать метод superFindAll () так, как вы хотел издеваться над родительским классом один. Это не идеальное решение с точки зрения покрытия и видимости, но оно должно выполнять свою работу, и его легко применять.

 public Childservice extends BaseService {
    public void save(){
        //some code
        superSave();
    }

    void superSave(){
        super.save();
    }
}
0 голосов
/ 10 июля 2013

Причина в том, что ваш базовый класс не является общедоступным, поэтому Mockito не сможет перехватить его из-за видимости, если вы измените базовый класс на public или @Override в подклассе (как public), то 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());
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...