Тестовые "прокси";хороший TDD или кодовый запах? - PullRequest
4 голосов
/ 05 января 2011

Когда я пишу тесты для определенных типов объектов, таких как элементы пользовательского интерфейса, такие как Forms или UserControls, я часто изменяю свой шаблон TDD; вместо того, чтобы идти сначала в тест, я определяю и размечаю элементы управления формы, чтобы обеспечить «скелет», затем начинаю писать поведенческие тесты (привязка данных / «открепление», поведение в режиме отображения и т. д.). При этом я сталкиваюсь с непубличными членами. Я также сталкиваюсь с той же проблемой с другими поведенческими методами; Я могу захотеть сосредоточиться и использовать логику в каком-то частном помощнике, вызываемом другим методом, прежде чем беспокоиться об использовании другого метода и его поведении.

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

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

Преимущества, которые я вижу:

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

Недостатки, которые я вижу:

  • Количество классов увеличивается, с дополнительным уровнем для разработки только для целей тестирования.
  • Необходимо позаботиться о том, чтобы каким-то образом не использовать тестовый прокси в производственном коде (обычно это делает конструктор или весь внутренний класс)
  • Не «чистый» модульный тест, поскольку на некотором уровне вы зависите от интеграции между прокси-сервером и реальным тестируемым объектом.

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

Ответы [ 3 ]

2 голосов
/ 05 января 2011

Моя первая реакция на этот паттерн заключается в том, что вы преуменьшаете значение 'D' в TDD. Ваш код протестирован, но эти тесты не влияют на ваш дизайн, поэтому код, с которым вы работаете, имеет другую структуру, чем если бы вы сначала написали тесты. Структура с более недоступным частным государством, чем необходимо. В общем, я утверждаю, что если вы не можете протестировать поведение своего класса с помощью общедоступных интерфейсов, то вы либо пишете тест, который не имеет смысла (тестирование деталей реализации), либо у вас плохо спроектированный публичный интерфейс.

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

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

MSTest использует этот метод для тестирования частных методов.Visual Studio помещает все тесты в отдельный тестовый проект и создает класс «Accessor».Этот класс является подклассом, где все закрытые члены публикуются.Поскольку этот подкласс находится в тестовом проекте, он недоступен в тестируемой сборке.Я думаю, что это жизнеспособный шаблон для тестирования частных методов и может быть реализован вручную в среде TDD, если вы не используете Visual Studio и MSTest.

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...