Какой смысл проверять, сколько раз функция вызывается с помощью Mockito? - PullRequest
0 голосов
/ 07 сентября 2018

В моем понимании, тестирование кода заключается в проверке правильности результатов, например, в калькуляторе. Мне нужно написать контрольный пример, чтобы проверить, равен ли результат 1 + 1 2.

Но я прочитал много тестов о том, сколько раз вызывается метод. Я очень смущен этим. Лучший пример - это то, что я только что видел в Spring in Action :

public class BraveKnight implements Knight {
    private Quest quest;
    public BraveKnight(Quest quest) { 
        this.quest = quest; 
    }
    public void embarkOnQuest() {
        quest.embark(); 
    }
}

public class BraveKnightTest {
    @Test 
    public void knightShouldEmbarkOnQuest() { 
        Quest mockQuest = mock(Quest.class); 
        BraveKnight knight = new BraveKnight(mockQuest); 
        knight.embarkOnQuest(); 
        verify(mockQuest, times(1)).embark(); 
    }
}

Я действительно понятия не имею, зачем им нужно проверять, что функция embark() вызывается один раз. Не думаете ли вы, что embark() обязательно будет вызван после вызова embarkOnQuest()? Или произойдут некоторые ошибки, и в журналах появятся сообщения об ошибках, в которых указан номер строки ошибки, которые могут помочь мне быстро найти неправильный код.

Так какой смысл проверять как выше?

Ответы [ 4 ]

0 голосов
/ 08 сентября 2018

Я действительно понятия не имею, зачем им нужно проверять запуск () функция вызывается один раз

Проверка вызова на макете определенное количество раз является стандартным способом работы Mockito при вызове Mockito.verify(). На самом деле это:

verify(mockQuest, times(1)).embark(); 

это просто многословный способ написать:

verify(mockQuest).embark(); 

В общем, проверка для одного вызова на макете - это то, что вам нужно .
В некоторых необычных сценариях вы можете убедиться, что метод вызывался определенное количество раз (более одного).
Но вы хотите избежать использования столь специфических проверок.
На самом деле вы даже хотите использовать проверку как можно меньше.

Если вам нужно использовать проверку и, кроме количества вызовов в макете, это обычно означает две вещи: максимизированная зависимость слишком сильно связана с классом в test и / или тестируемый метод выполняет слишком много унитарных задач, которые вызывают только побочные эффекты.
Тест не является обязательным для чтения и обслуживания. Это как если бы вы кодировали фиктивный поток в проверочных вызовах.
И, как следствие, это также делает тесты более хрупкими, поскольку он проверяет детали вызова, а не общую логику и состояния.
В большинстве случаев рефакторинг является средством защиты и отменяет требование указать номер вызова.

Я не говорю, что это никогда не требуется, но использую его только потому, что это единственный достойный выбор для тестируемого класса.

0 голосов
/ 07 сентября 2018

Необходимость проста: убедиться, что было сделано правильное количество вызовов. Существуют сценарии, в которых вызовы методов не должны происходить, и другие, в которых они должны происходить больше или меньше значения по умолчанию.

Рассмотрим следующую модифицированную версию embarkOnQuest:

public void embarkOnQuest() {
    quest.embark(); 
    quest.embarkAgain(); 
}

И предположим, что вы тестируете случаи ошибок для quest.embark():

@Test 
public void knightShouldEmbarkOnQuest() { 
    Quest mockQuest = mock(Quest.class); 
    Mockito.doThrow(RuntimeException.class).when(mockQuest).embark();
    ...
}

В этом случае вы хотите убедиться, что quest.embarkAgain НЕ вызывается (или вызывается 0 раз):

verify(mockQuest, times(0)).embarkAgain(); //or verifyZeroInteractions

Конечно, это еще один простой пример. Есть много других примеров, которые можно добавить:

  • Соединитель базы данных, который должен кэшировать записи при первой выборке, можно сделать несколько вызовов и убедиться, что соединение с базой данных было вызвано только один раз (на тестовый запрос)
  • Одноэлементный объект, который выполняет инициализацию при загрузке (или лениво), можно проверить, что вызовы, связанные с инициализацией, выполняются только один раз.
0 голосов
/ 07 сентября 2018

Рассмотрим следующий код:

public void saveFooIfFlagTrue(Foo foo, boolean flag) {
    if (flag) {
        fooRepository.save(foo);
    }
}

Если вы не проверяете, сколько раз вызывается fooRepository.save(), то как узнать, что этот метод делает то, что вам нужно?

Это относится к другим пустым методам. Если нет возврата к методу и, следовательно, нет ответа на проверку, то проверка того, какие другие методы вызваны, является хорошим способом проверки правильности поведения метода.

0 голосов
/ 07 сентября 2018

Хороший вопрос. Вы подчеркиваете, что насмешка может быть слишком сложной, когда вы можете просто проверить результаты. Однако существуют ситуации, когда это приводит к более надежным испытаниям.

Например, если метод должен выполнить вызов внешнего API, есть несколько проблем с простым тестированием результата:

  • Сетевой ввод / вывод медленный. Если у вас много таких проверок, это замедлит ваш тестовый случай
  • Любое подобное прохождение туда и обратно должно зависеть от кода, выполняющего запрос, API и кода, интерпретирующего ответ API, для правильной работы. Это много точек отказа для одного теста.
  • Если произойдет что-то глупое и вы случайно сделаете несколько запросов, это может вызвать проблемы с производительностью вашей программы.

Чтобы ответить на ваши подвопросы:

Не думаете ли вы, что embark () обязательно будет вызван после вызова embarkOnQuest ()?

Тесты также имеют значение, позволяя вам проводить рефакторинг, не беспокоясь о том, что что-то сломается. Это очевидно сейчас, да. Будет ли это очевидно через 6 месяцев?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...