Ответ 1
Многие из насмешливых фреймворков сближают концепции макетов и заглушек с тем, что их можно считать функционально почти одинаковыми. Однако с концептуальной точки зрения я обычно стараюсь следовать этому соглашению:
- Mock: только когда вы явно пытаетесь проверить поведение тестируемого объекта (т.е. ваш тест говорит, что этот объект должен вызывать этот объект).
- Stub. Когда вы пытаетесь протестировать некоторые функции/поведение, но для того, чтобы получить эту работу, вам нужно полагаться на некоторые внешние объекты (т.е. ваш тест говорит, что этот объект должен что-то делать, но как побочный эффект, он может назвать этот объект)
Это становится яснее, когда вы убедитесь, что каждый из ваших модулей тестирует только одно. Конечно, если вы попытаетесь протестировать все в одном тесте, тогда вы также можете ожидать все. Но только ожидая того, что проверяет конкретный unit test, ваш код намного яснее, потому что вы можете сразу увидеть, что цель теста.
Другим преимуществом этого является то, что вы будете немного более изолированы от изменений и получите лучшие сообщения об ошибках, когда изменение приведет к перерыву. Другими словами, если вы измените часть своей реализации, вы, скорее всего, получите только один разрыв теста, который покажет вам именно то, что сломано, а не целый набор тестов и просто создает шум.
Изменить. Это может быть яснее на основе надуманного примера, когда объект калькулятора проверяет все дополнения к базе данных (в псевдокоде)...
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
и не заботимся о том, произошло ли событие аудита или нет, но мы знаем, что калькулятор не будет работать с аудитDB так что мы просто остановим его, чтобы дать нам минимум функциональности, чтобы наш конкретный тестовый пример работал. Во втором тесте мы специально проверяем, что, когда вы выполняете Add
, происходит событие аудита, поэтому мы используем ожидания (обратите внимание, что нам даже неважно, каков результат, поскольку это не то, что мы тестируем).
Да, вы могли бы объединить два случая в один, сделать ожидания и утверждать, что ваш результат равен 3, но затем вы тестируете два случая в одном unit test. Это сделало бы ваши тесты более хрупкими (поскольку там была большая площадь поверхности вещей, которые могли бы измениться, чтобы сломать тест) и менее ясны (поскольку, когда объединенный тест терпит неудачу, не сразу становится очевидным, в чем проблема... добавление не работает, или аудит не работает?)