Является ли класс, сложный для unit test плохо разработан?

Теперь я выполняю модульное тестирование приложения, которое было написано в течение года, прежде чем я начал тщательно тестировать блок-тестирование. Я понял, что классы, которые я написал, трудно unit test по следующим причинам:

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

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

Есть ли эта причина для воды? Каковы ваши впечатления?

Ответы

Ответ 1

Да, вы правы. Класс, который not unit testable hard to unit test (почти всегда) не очень хорошо разработан (есть исключения, как всегда, но они редки - IMHO лучше не пытаться объяснить проблему так). Отсутствие единичных тестов означает, что их сложнее поддерживать - у вас нет способа узнать, нарушила ли вы существующую функциональность всякий раз, когда вы что-то изменяете.

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

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

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

Ответ 2

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

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

Ответ 3

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

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

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

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

Ответ 4

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

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

Ответ 5

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

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

Обновление: позвольте мне добавить это до того, как меня обвиняют в том, что дизайн для * testability не важен

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

Ответ 6

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

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

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

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

Ответ 7

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

Ответ 8

I может иметь идеальное решение для вас рассмотреть... Частный аксессуар

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

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

Тогда это базовое утверждение и верификация для требуемых условий как нормальные.