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