Вопрос

Я использую NMock2, и я разработал следующие классы NMock для представления некоторых общих концепций mock framework:

  • Expect:это указывает, что должен возвращать издеваемый метод, и говорит, что вызов должен произойти, иначе тест завершится неудачей (когда сопровождается вызовом VerifyAllExpectationsHaveBeenMet()).

  • Stub:это указывает, что должен возвращать издеваемый метод, но не может привести к сбою теста.

Так что же мне когда делать?

Это было полезно?

Решение

Множество фреймворков-макетов сближают концепции макетов и заглушек до такой степени, что их можно считать функционально почти одинаковыми.Однако с концептуальной точки зрения я обычно стараюсь следовать этому соглашению:

  • Издеваться:Только тогда, когда вы явно пытаетесь проверить поведение тестируемого объекта (т.е.ваш тест говорит, что этот объект должен вызывать этот объект).
  • Заглушка:Когда вы пытаетесь протестировать некоторую функциональность / поведение, но для того, чтобы это заработало, вам нужно полагаться на некоторые внешние объекты (т.е.ваш тест говорит, что этот объект должен что-то делать, но в качестве побочного эффекта он может вызвать этот объект)

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

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

Редактировать:Это может быть понятнее на основе надуманного примера, где объект calculator проверяет все добавления в базу данных (в псевдокоде)...

public void CalculateShouldAddTwoNumbersCorrectly() {
    var auditDB = //Get mock object of Audit DB
    //Stub out the audit functionality...
    var calculator = new Calculator(auditDB);
    int result = calculator.Add(1, 2);
    //assert that result is 3
}

public void CalculateShouldAuditAddsToTheDatabase() {
    var auditDB = //Get mock object of Audit DB
    //Expect the audit functionality...
    var calculator = new Calculator(auditDB);
    int result = calculator.Add(1, 2);
    //verify that the audit was performed.
}

Итак, в первом тестовом примере мы тестируем функциональность Add методу & все равно, произойдет событие аудита или нет, но мы случайно знаем, что калькулятор не будет работать без ссылки на auditDB, поэтому мы просто отключаем его, чтобы предоставить нам минимум функциональности для работы нашего конкретного тестового примера.Во втором тесте мы специально проверяем, что, когда вы выполняете Add, происходит событие аудита, поэтому здесь мы используем ожидания (обратите внимание, что нас даже не волнует, каков результат, поскольку это не то, что мы тестируем).

Да, вы могли бы объединить два случая в один, выполнить ожидания и утверждать, что ваш результат равен 3, но тогда вы тестируете два случая в одном модульном тестировании.Это сделало бы ваши тесты более хрупкими (поскольку имеется большая площадь поверхности объектов, которые могут измениться, чтобы нарушить тест) и менее четкими (поскольку при сбое объединенного теста не сразу становится очевидно, в чем проблема..добавление не работает или аудит не работает?)

Другие советы

"Ожидайте действий, заглушки запросов".Если вызов должен изменить состояние мира за пределами тестируемого объекта, то сделайте это ожидаемым - вам важно, как он вызывается.Если это просто запрос, вы можете вызвать его один или шесть раз без изменения состояния системы, затем заглушите вызов.

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

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

Для получения более подробной информации ознакомьтесь этот подкаст Хансельмана и Ошерова (автор книги "Искусство модульного тестирования")

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top