Класс, который тверд к модульному тесту, плохо разработанному? [закрытый]

18
задан John 17 April 2010 в 17:12
поделиться

8 ответов

Да, вы правы. Класс, который не поддается модульному тестированию , трудно поддающийся модульному тесту, (почти всегда) плохо спроектирован (есть исключения, как всегда, но они редки - ИМХО, лучше не пытаться объяснять проблему этим способ). Отсутствие модульных тестов означает, что их труднее поддерживать - у вас нет возможности узнать, нарушили ли вы существующую функциональность, когда вы что-либо изменяете в ней.

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

TDD - это не просто способ проверить ваш код, это еще и другой способ проектирования. Эффективное использование - и размышления об использовании - ваших собственных классов и интерфейсов с самого первого момента может привести к совершенно иному дизайну, нежели традиционный способ «кодировать и молиться». Одним из конкретных результатов является то, что, как правило, большая часть вашего критического кода изолирована от границ вашей системы, т.е. есть оболочки / адаптеры, которые можно скрыть, например. конкретная БД из остальной части системы и «интересный» (т. е. тестируемый) код находятся не в этих оболочках - они максимально просты - а в остальной части системы.

Итак, если у вас есть куча кода без модульных тестов и вы хотите его охватить, у вас есть проблема. Мокирующие фреймворки могут очень помочь, но все же писать модульные тесты для такого кода - заноза в заднице. Хороший источник методов для решения таких проблем (широко известный как унаследованный код ) - это Эффективная работа с унаследованным кодом Майкла Фезерса.

13
ответ дан 30 November 2019 в 06:42
поделиться

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

Вам следует изучить фреймворки Mocking, чтобы смоделировать базу данных и другие классы, на которые опирается этот класс, чтобы вы могли лучше контролировать тестирование.

11
ответ дан 30 November 2019 в 06:42
поделиться

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

1
ответ дан 30 November 2019 в 06:42
поделиться

Хотя не существует способа определить, является ли класс «хорошо спроектированным» или нет, по крайней мере первое, о чем вы упоминаете, обычно является признаком сомнительного дизайна.

Вместо того, чтобы полагаться на базу данных, класс, который вы тестируете, должен иметь зависимость от объекта, единственной обязанностью которого является получение этих данных, возможно, используя такой шаблон, как Repository или DAO.

Что касается второй причины, она не обязательно указывает на плохой дизайн класса; это может быть проблема с дизайном тестов (отсутствие супертипа приспособлений или помощников, где вы можете настроить зависимости, используемые в нескольких тестах) и / или общей архитектурой (то есть без использования фабрик или инверсии управления для внедрения соответствующих зависимостей )

Кроме того, вам, вероятно, следует использовать не «настоящие» объекты для своих зависимостей, а тестовые двойники. Это поможет вам убедиться, что вы тестируете поведение одного этого класса, а не его зависимостей. Предлагаю вам изучить макетные фреймворки.

1
ответ дан 30 November 2019 в 06:42
поделиться

Я обнаружил, что внедрение зависимостей - это шаблон проектирования, который больше всего помогает сделать мой код тестируемым (и, часто, также повторно используемым и адаптируемым к контекстам, отличным от исходного, который я его разработал. для). Мои коллеги, использующие Java, обожают Guice ; Я в основном программирую на Python, так что обычно я делаю инъекцию зависимостей «вручную», поскольку утиная типизация упрощает эту задачу; но это правильный фундаментальный DP как для статических, так и для динамических языков (не позволяйте мне начинать с «исправления обезьян» ... скажем так, это не мой любимый ;-).

Когда ваш класс будет готов принять свои зависимости «извне», вместо того, чтобы жестко запрограммировать их, вы, конечно, можете использовать поддельные или фиктивные версии зависимостей, чтобы упростить и ускорить выполнение тестирования - но это также открывает другие возможности. Например, если текущее состояние класса является сложным и трудно настраиваемым, рассмотрите шаблон проектирования Состояние : вы можете провести рефакторинг проекта так, чтобы состояние находилось в отдельной зависимости (которую вы можете настроить и ввести по желанию), а сам класс в основном отвечает за поведение (обновление состояния).

Конечно, проводя рефакторинг таким образом, вы будете вводить все больше и больше интерфейсов (абстрактных классов, если вы используете C ++) - но это совершенно нормально: это отличный принцип «программировать интерфейс , а не реализация ".

Итак, отвечая на ваш вопрос напрямую, вы правы: сложность тестирования определенно эквивалентна тому, что крайнее программирование называет «запахом кода».С другой стороны, есть довольно ясный способ реорганизовать эту проблему - вам не обязательно иметь идеальный дизайн для начала (к счастью! -), но вы можете улучшать его по ходу дела. Я бы порекомендовал книгу Refactoring to Patterns как хорошее руководство для этой цели.

11
ответ дан 30 November 2019 в 06:42
поделиться

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

В вашем случае, возможно, вы можете имитировать внешние зависимости для запуска реальных модульных тестов (изолированно).

6
ответ дан 30 November 2019 в 06:42
поделиться

Я возьму другой подход: код просто не предназначен для тестируемости , но это не означает, что он обязательно плохо спроектирован . Дизайн является продуктом конкурирующих * способностей , из которых проверяемость является лишь одним из них. Каждое решение о кодировании увеличивает некоторые из * категорий , уменьшая другие. Например, проектирование для тестируемости обычно вредит его простоте / удобочитаемости / понятности (потому что это добавляет сложности). Хороший дизайн благоприятствует наиболее важным * свойствам вашей ситуации.

Ваш код не плохой , он просто максимизирует другие * возможности , кроме тестируемости . : -)

Обновление: позвольте мне добавить это, прежде чем меня обвинят в том, что проектирование для * проверяемость не имеет значения

Уловка, конечно же, заключается в разработке и кодировании для максимизации пользы * способности , особенно важные. Какие из них являются важными, зависит от вашей ситуации. По моему опыту в моих ситуациях, проектирование с учетом тестируемости было одной из наиболее важных * способностей .

3
ответ дан 30 November 2019 в 06:42
поделиться

Я мог бы предложить вам идеальное решение ... частный аксессор

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

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

Затем это базовое утверждение и проверка для желаемых условий как обычно.

0
ответ дан 30 November 2019 в 06:42
поделиться
Другие вопросы по тегам:

Похожие вопросы: