Я использую Mockito в некоторых тестах.
У меня есть следующие занятия:
class BaseService {
public void save() {...}
}
public Childservice extends BaseService {
public void save(){
//some code
super.save();
}
}
Я хочу высмеять только второй вызов ( super.save
) из ChildService
. Первый вызов должен вызывать настоящий метод. Есть способ сделать это?
Ответы:
Нет, Mockito этого не поддерживает.
Возможно, это не тот ответ, который вы ищете, но то, что вы видите, является признаком неприменения принципа дизайна:
Если вы извлечете стратегию вместо расширения суперкласса, проблема исчезнет.
Однако, если вам не разрешено изменять код, но вы все равно должны его протестировать, и таким неудобным способом, надежда еще есть. С помощью некоторых инструментов АОП (например, AspectJ) вы можете вплести код в метод суперкласса и полностью избежать его выполнения (фу). Это не работает, если вы используете прокси, вам нужно использовать модификацию байт-кода (либо изменение времени загрузки, либо изменение времени компиляции). Существуют фиктивные фреймворки, которые также поддерживают этот тип трюков, например PowerMock и PowerMockito.
Я предлагаю вам заняться рефакторингом, но если это не вариант, вас ждут серьезные взломы.
источник
//some codes
код в метод, который можно протестировать отдельно.Если у вас действительно нет выбора для рефакторинга, вы можете имитировать / заглушить все в вызове супер-метода, например
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(); }
источник
BaseService
абстрактно, хотя я не понимаю, почему это имеет значение.super.validate()
Рассмотрите возможность рефакторинга кода из метода ChildService.save () в другой метод и протестируйте этот новый метод вместо тестирования ChildService.save (), таким образом вы избежите ненужного вызова супер-метода.
Пример:
class BaseService { public void save() {...} } public Childservice extends BaseService { public void save(){ newMethod(); super.save(); } public void newMethod(){ //some codes } }
источник
создайте метод защищенного пакета (предполагается, что тестовый класс находится в том же пакете) в подклассе, который вызывает метод суперкласса, а затем вызовите этот метод в своем переопределенном методе подкласса. затем вы можете установить ожидания для этого метода в своем тесте с помощью шаблона шпиона. не очень, но определенно лучше, чем иметь дело со всеми настройками ожидания для супер-метода в вашем тесте
источник
Даже если я полностью согласен с ответом 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
источник
Причина в том, что ваш базовый класс не является общедоступным, тогда 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()); }
источник
Возможно, самый простой вариант, если наследование имеет смысл, - это создать новый метод (частный пакет ??) для вызова super (давайте назовем его superFindall), шпионить за реальным экземпляром, а затем издеваться над методом superFindAll () так, как вы хотели имитировать родительский класс один. Это не идеальное решение с точки зрения охвата и видимости, но оно должно работать и его легко применять.
public Childservice extends BaseService { public void save(){ //some code superSave(); } void superSave(){ super.save(); } }
источник