Я делаю что-то принципиально неправильное в своих модульных тестах?

Прочитав интересную статью о тестировании модульного тестирования вместо состояния, я понял, что мои модульные тесты часто тесно связаны с моим кодом, потому что я использую mocks. Я не могу писать текстовые тесты без издевок, но факт состоит в том, что эти mocks связывают мой unit test с моим кодом из-за ожидаемых вызовов и возврата.

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

Является ли эта проблема неотъемлемой частью использования mocks, и я должен научиться жить с ней, или я делаю что-то принципиально неправильное? Пожалуйста, просветите меня:) Разумные примеры, пришедшие с объяснением, приветствуются, конечно же.

Ответ 1

когда я создаю тест, который использует макет, Я записываю все вызовы на конкретные mock и присваивать возвращаемые значения

Похоже, вы можете переопределить ожидания.

Постарайтесь создать как можно меньше кода установки в своих тестах: заглушить (а не ожидать) все поведение, которое не относится к текущему тесту, и указывать только возвращаемые значения, которые абсолютно необходимы для выполнения вашей тестовой работы.

Этот ответ содержит краткий пример (а также альтернативное, более подробное объяснение).

Ответ 2

Мой опыт состоит в том, чтобы использовать mocks только в bounderies (sub) систем. Если у меня есть два класса, которые сильно связаны, я не издеваюсь над ними, но проверяю их вместе. Примером может быть составной и посетитель. Если я тестирую конкретного посетителя, я не использую макет для композита, а создаю настоящие композиты. Можно утверждать, что это не unit test (зависит от определения того, что является единицей). Но это не имеет большого значения. Я пытаюсь достичь:

  • Запись читаемых тестов (тесты без макетов в большинстве случаев более легко читаются).
  • Протестируйте только сфокусированную область кода (в примере посетитель-конкретизирующий и соответствующая часть композита).
  • Напишите быстрые тесты (пока я создаю несколько классов, в примере конкретных композитов, это не проблема... следите за переходными творениями).

Только если я столкнулся с границей подсистемы, я использую mocks. Пример: у меня есть составной компонент, который может отображать себя в рендерере. Я бы издевался над рендерером, если я тестирую логику рендеринга композита.

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

Ответ 3

Если вы исправляете тесты, потому что они ломаются, вы не используете их по назначению.

Если поведение метода изменяется, в тестовой разработке вы сначала должны изменить тест, чтобы ожидать нового поведения, а затем реализовать новое поведение.

Ответ 4

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