2010-04-11 2 views
4

Я только начал практиковать TDD в своих проектах. Я разрабатываю проект, используя php/zend/mysql и phpunit/dbunit для тестирования. Я просто немного отвлекся на идею инкапсуляции и подхода, основанного на тестах. Моя идея инкапсуляции заключается в том, чтобы скрыть доступ к нескольким функциональным возможностям. Чтобы сделать его более понятным, частные и защищенные функции напрямую не проверяются (если только вы не создадите публичную функцию для его вызова).TDD и конфликт приоритета инкапсуляции

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

ответ

8

Существует довольно стандартный ответ на этот вопрос в кругах TDD. Если в классе есть функциональность, которую вы оба хотите скрывать и тестировать напрямую, вы должны sprout a class с этой функциональностью. Это отличный пример того, как TDD улучшает ваш дизайн.

В оригинальном классе эта посторонняя функциональность исчезла, завернутая в проросший класс, поэтому первоначальный класс «проще» и лучше соответствует Single Responsibility Principle. В проросшем классе извлеченная функциональность - это raison d'etre, поэтому для него является общедоступным, поэтому его можно тестировать без тестовых изменений.

+0

Точно то, что мне было нужно. Класс sprout для меня новичок (также другие модели xunit). Спасибо alot – Hanseh

+2

Хорошая книга для этого: ** Эффективная работа с устаревшим кодом. ** – Gutzofter

7

С уважением, прекрасный ответ Карла Манастера, есть некоторые недостатки, которые вы должны хотя бы рассмотреть перед тем, как отправиться на путь, предложенный Карлом.

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

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

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

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

По этим причинам, я не согласен с Карлом, что его предложение,»... отличный пример того, как TDD улучшает дизайн.»

Кроме того, он утверждает, «В оригинальном классе, что постороннее функциональность ушел , завернутый в проросший класс, поэтому дизайн первоначального класса проще и лучше соответствует принципу единой ответственности.

Я бы сказал, что перемещаемая функциональность вовсе не «Внешняя.«Кроме того,« Упрощенный »не является четко определенным: конечно, может быть, что простота класса обратно пропорциональна его размеру, но это не значит, что система простейших возможных классов будет самой простой возможной системой: если бы это было так, все классы содержали бы только один метод, и система имела бы огромное количество классов; удаление этого иерархического слоя из нескольких методов внутри классов, можно утверждать, сделает систему намного сложнее.

Принцип единой ответственности (СРП), кроме того, является, как известно, субъективным и полностью зависит от уровня абстракции наблюдателя. Совсем не так, что удаление метода из класса автоматически улучшает его соответствие SRP. Класс принтера с 10 методами имеет единственную ответственность за печать на уровне абстракции класса. Одним из его методов может быть checkPrinterConnected(), и может быть checkPaper(); на уровне метода это явно отдельные обязанности, но они автоматически не предполагают, что класс должен быть разбит на другие классы.

Carl заканчивает: «В классе прорастания извлеченная функциональность - это его raison d'etre, поэтому для него является общедоступным, и поэтому он может быть проверен без тестовых изменений». Важность функциональности (это смысл, d'etre-ness) не является основанием для уместности его публичности. Основой для приемлемости публичности функциональности является минимизация интерфейса, открытого для клиента, так что функциональность класса доступна для использования, в то время как независимость клиента от реализации функциональности максимизируется. Конечно, если вы перемещаете только один метод в проросший класс, тогда он должен быть общедоступным. Однако, если вы перемещаете более одного метода, вы должны сделать эти методы общедоступными, что имеет важное значение для успешного использования клиентом класса: эти общедоступные методы могут быть гораздо менее важными, чем некоторые частные методы, из которых вы хотите защитить свои клиент. (В любом случае, я не поклонник этого, «Raison-d'etre», фраза как важность метода также не определена.)

Альтернативный подход к предложению Карла зависит от насколько велика ваша система для роста. Если он вырастет до менее чем нескольких тысяч классов, то вы можете подумать о том, чтобы сценарий скопировал исходный код в новый каталог, измените все события «private» на «public» в этом скопированном источнике, а затем напишите тесты против скопированного источника. Это имеет недостаток времени, необходимого для копирования кода, но преимущество сохранения инкапсуляции исходного источника, но при этом все методы проверяются в скопированной версии.

Ниже приведен сценарий, который я использую для этой цели.

С уважением,

Ed Кируон

!/Bin/Баш

гт -rf код-копия

эхо Создание кода ксерокопию ...

MkDir code- копия

cp -r ../www код-копия/

для i в find code-copy -name "*php" -follow; сделать

sed -i 's/private/public/g' $i 

сделали

PHP run_tests.PHP

+0

Спасибо .. Это еще одна хорошая Я буду ждать больше информации и буду заниматься своими исследованиями по этой теме. – Hanseh

+0

Будет ли это делать медленнее? – Hanseh

3

Я только что прочитал большую статью о давая фиктивные объекты привода вы дизайн:

http://www.mockobjects.com/files/usingmocksandtests.pdf

Когда Карл говорит: «Вы должны прорасти класс с этой функциональностью», автор этой статьи объяснить, как ваши тесты могут помочь вам, используя макет объектов, как вы можете спроектировать свой класс, чтобы вы: 1) не нужно беспокоиться о том, что вы не можете проверить личные детали и, что более важно, 2) как это улучшит ваш дизайн (Я перефразирую цитату Карла), открывая соавторы и роли с правильной ответственностью.

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

Вот еще одна статья с такой же подход:

http://www.methodsandtools.com/archive/archive.php?id=90

Цитата:

Многие люди, которые начинают с TDD борьбы с получить контроль над зависимостями. Чтобы проверить объект , вы выполняете какое-то поведение , а затем проверяете, находится ли объект в ожидаемом состоянии. Поскольку дизайн OO фокусируется на поведении , состояние объекта обычно скрыто (инкапсулировано). Чтобы быть в состоянии проверить, если объект ведет себя , как и ожидалось, иногда необходимо доступа внутреннее состояние и ввести специальные методы, чтобы выставить это состояние, как метод получения или собственности, получающего внутреннее состояние.

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

Группа быстрой разработки программного обеспечения пионеров из Соединенного Королевства были также борется с этим еще в 1999 Они должны были добавить дополнительные методы геттера для проверки состояния объектов. Их менеджеру не понравилось все это разрушение инкапсуляции и заявлено: я не хочу получать коэффициенты в коде ! (Маккиннон и др., 2000 & Freeman и др., 2004)

Команда пришла в голову идея сосредоточиться на взаимодействии, а не состоянии. Они создали специальный объект , чтобы заменить соавторов объектов, находящихся под тестом. Эти специальные объекты содержали спецификации для ожидаемых вызовов методов. Они назвали этими объектами, имитирующими объекты, или макеты для краткости. Исходные идеи были уточнены, в результате получилось несколько фреймворков для всех распространенных языков программирования : Java (jMock, EasyMock, Mockito).NET (NMock, RhinoMocks), Python (PythonMock, Mock.py, Ruby (мокко, RSpec), C++ (mockpp, amop). См www.mockobjects.com для получения дополнительной информации и ссылок.

+0

будет давать эту попытку. спасибо – Hanseh

+0

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