Я делаю что-то принципиально неправильное в своих модульных тестах?
Прочитав интересную статью о тестировании модульного тестирования вместо состояния, я понял, что мои модульные тесты часто тесно связаны с моим кодом, потому что я использую mocks.
Я не могу писать текстовые тесты без издевок, но факт состоит в том, что эти mocks связывают мой unit test с моим кодом из-за ожидаемых вызовов и возврата.
Например, когда я создаю тест, который использует макет, я записываю все вызовы на конкретный макет и присваиваю возвращаемые значения.
Теперь, когда я изменяю реализацию фактического кода по какой-либо причине, многие тесты ломаются, потому что этот вызов не ожидался от макета, заставляя меня также обновлять unit test и эффективно заставляя меня выполнять каждое изменение дважды.,
Это происходит очень часто.
Является ли эта проблема неотъемлемой частью использования mocks, и я должен научиться жить с ней, или я делаю что-то принципиально неправильное?
Пожалуйста, просветите меня:)
Разумные примеры, пришедшие с объяснением, приветствуются, конечно же.
Ответы
Ответ 1
когда я создаю тест, который использует макет, Я записываю все вызовы на конкретные mock и присваивать возвращаемые значения
Похоже, вы можете переопределить ожидания.
Постарайтесь создать как можно меньше кода установки в своих тестах: заглушить (а не ожидать) все поведение, которое не относится к текущему тесту, и указывать только возвращаемые значения, которые абсолютно необходимы для выполнения вашей тестовой работы.
Этот ответ содержит краткий пример (а также альтернативное, более подробное объяснение).
Ответ 2
Мой опыт состоит в том, чтобы использовать mocks только в bounderies (sub) систем. Если у меня есть два класса, которые сильно связаны, я не издеваюсь над ними, но проверяю их вместе. Примером может быть составной и посетитель. Если я тестирую конкретного посетителя, я не использую макет для композита, а создаю настоящие композиты. Можно утверждать, что это не unit test (зависит от определения того, что является единицей). Но это не имеет большого значения. Я пытаюсь достичь:
- Запись читаемых тестов (тесты без макетов в большинстве случаев более легко читаются).
- Протестируйте только сфокусированную область кода (в примере посетитель-конкретизирующий и соответствующая часть композита).
- Напишите быстрые тесты (пока я создаю несколько классов, в примере конкретных композитов, это не проблема... следите за переходными творениями).
Только если я столкнулся с границей подсистемы, я использую mocks. Пример: у меня есть составной компонент, который может отображать себя в рендерере. Я бы издевался над рендерером, если я тестирую логику рендеринга композита.
Тестирование, а не состояние выглядит перспективным сначала, но в целом я бы тестировал состояние, так как полученные тесты легко поддерживать. Мошки - это пушка. Не тресните орех кувалдой.
Ответ 3
Если вы исправляете тесты, потому что они ломаются, вы не используете их по назначению.
Если поведение метода изменяется, в тестовой разработке вы сначала должны изменить тест, чтобы ожидать нового поведения, а затем реализовать новое поведение.
Ответ 4
Несколько хороших ответов здесь уже, но для меня хорошим правилом является проверка требований метода, а не реализация. Иногда это может означать использование макетного объекта, потому что взаимодействие является требованием, но вам обычно лучше тестировать возвращаемое значение метода или изменение состояния объекта.