Когда ожидать и когда заглушки? - PullRequest
12 голосов
/ 10 июля 2009

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

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

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

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

Ответы [ 3 ]

17 голосов
/ 10 июля 2009

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

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

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

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

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

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, но затем вы тестируете два случая в одном модульном тесте. Это сделало бы ваши тесты более хрупкими (поскольку есть большая площадь поверхности вещей, которые могли бы измениться, чтобы нарушить тест) и менее четкими (так как, когда объединенный тест не пройден, не сразу очевидно, в чем проблема ... не работает ли сложение, или аудит не работает?)

4 голосов
/ 06 января 2011

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

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

1 голос
/ 10 июля 2009

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

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

...