Является ли слишком большое внимание к преимуществам тестирования в целом плохо? - PullRequest
11 голосов
/ 24 мая 2009

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

Например, я рассматриваю очень сложное приложение, сделанное так благодаря широкому использованию шаблонов проектирования, которые чрезмерно благоприятствуют тестированию, например, IoC, DI, AOP и т.д ...
Теперь, как правило, мне нравятся эти вещи, но эта система должна была намного проще - хотя это не просто веб-интерфейс для CRUD на БД, она все же НЕ НАМНОГО сложнее (даже с учетом некоторых внутренних рабочих процессов). , процессы и т. д.). С другой стороны, просто просмотр кода становится основной болью в heinie, едва читаемой (даже если она хорошо написана), и его кодирование, должно быть, было болью.

Реализованная сложность является явным нарушителем KISS (принцип, а не группа) ... и "единственным" преимуществом является улучшенная тестируемость, использующая тестовые фреймворки и макеты и ...

Теперь, прежде чем вы, фанаты TDD, прыгните на меня, я не умаляю важности тестируемости, но ставлю под сомнение превосходство рассмотрения этой конкретной силы (против всех остальных).
Или я что-то пропустил?


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


Я чувствовал необходимость добавить эту цитату из Википедии на IoC :

В то время как опасность в процедурном программировании заключалась в том, чтобы заканчиваться кодом спагетти, опасность при использовании Inversion of Control заканчивается кодом макарон

Да, это точно выражает мои чувства: D

Ответы [ 13 ]

13 голосов
/ 24 мая 2009

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

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

TDD отчасти о тестировании, отчасти о дизайне. Плохое TDD может слишком сильно склоняться к гибкости или простоте. Это может привести к слишком большой гибкости. Объекты становятся более тестируемыми и часто более простыми, но сложная проблема предметной области затем выталкивается из объектов во взаимодействие объектов. Мы приобрели гибкость, и наивный взгляд может показаться, что мы приобрели простоту, потому что наши объекты проще. Сложность, однако, все еще там. Он перемещается из объектов в взаимодействие с объектами, где его сложнее контролировать. Здесь есть запахи кода, которые могут выступать в качестве красных флажков - система с сотнями мелких объектов и без больших объектов - это одно, а множество объектов с однострочными методами - это другое.

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

Теперь TDD по своей природе не выбьет вас из равновесия в любом направлении, при условии, что он хорошо применяется. Используйте другие методы, чтобы держать вас на пути. Например, нарисуйте, что вы делаете, прежде чем сделать это. Очевидно, не все время. Некоторые вещи слишком просты для этого. Некоторые изображения заслуживают того, чтобы их сохранить, некоторые - просто наброски, которые помогают нам визуализировать проблему, и мы, в различной степени, в основном изучаем визуальные эффекты. Если вы не можете нарисовать картину проблемы, вы не понимаете ее.

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

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

Итак, мой ответ на ваш вопрос «да»: протестируйте все, не забывая другие хорошие принципы.

Любая хорошая практика сбивает вас с курса, если она не сбалансирована с другими хорошими практиками.

6 голосов
/ 24 мая 2009

"Я что-то пропустил?"

Да.

Это работает, не так ли?

И, что более важно, вы можете продемонстрировать, что это работает.

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

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


Редактировать

«Сложность» - скользкая концепция. Есть объективные меры сложности. Что более важно, так это ценность, создаваемая увеличением сложности. Повышение сложности обеспечивает тестируемость, конфигурируемость, позднее связывание, гибкость и адаптивность.

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

«Тестируемость» тоже скользкая. Могут быть объективные меры тестируемости. В основном, однако, они передаются для проверки покрытия. И тестовое покрытие не очень значимый показатель. Как меняется вероятность сбоя в производстве в зависимости от покрытия тестами? Это не так.

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

Выделение «тестируемости» как основной причины «сложности» упускает из виду.

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

6 голосов
/ 24 мая 2009

Чтобы ответить на ваш общий вопрос, я бы сказал «все в меру». Акцент на тестируемости - это, конечно, отличная вещь. Но не тогда, когда речь идет об исключении, скажем, читаемого кода или логического API.

4 голосов
/ 25 мая 2009

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

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

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

3 голосов
/ 26 мая 2009

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

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

Я говорю о тестируемости здесь . Джеймс Бах говорит об этом здесь .

--- Майкл Б.

3 голосов
/ 25 мая 2009

«Или я что-то пропустил?»

Существует прямая связь между вопросом, насколько тестируемый код является сложным. Если бы это был ваш опыт, я бы сказал, вы делаете это неправильно .

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

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

Тесты не должны быть такими же чистыми и хорошо продуманными, как код, который они тестируют. Часто лучше делать то, что обычно было бы неприятным взломом в тесте, чем делать много изменений в коде. Это особенно хорошо для тестирования на сбой. Нужно смоделировать сбой подключения к базе данных? Кратко замените метод connect () на метод, который всегда дает сбой. Нужно знать, что происходит, когда диск заполняется? Замените метод открытия файла на метод, который не удался. Некоторые языки хорошо поддерживают эту технику (Ruby, Perl), другие не очень. То, что обычно является ужасным стилем, становится мощной техникой тестирования, прозрачной для вашего производственного кода.

Одна вещь, которую я определенно скажу, - это никогда не запускать код в производство, который полезен только для тестирования. Что-нибудь вроде if( TESTING ) { .... } прямо сейчас. Это просто загромождает код.

3 голосов
/ 24 мая 2009

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

Я на самом деле довольно устойчив к добавлению сложности, где я могу избежать этого. Например, я еще не перешел к платформе DI / IoC, предпочитая вводить зависимости вручную только в тех случаях, когда это необходимо для тестирования. С другой стороны, когда я, наконец, принял практику, которая «увеличивает» сложность - например, насмешливые фреймворки - я обнаружил, что уровень сложности на самом деле меньше, чем я боялся, а выгода больше, чем я себе представлял. Возможно, я в конечном итоге обнаружу, что это верно и для платформ DI / IoC, но я, вероятно, не пойду туда, пока у меня не будет достаточно небольшого проекта, чтобы экспериментировать, не откладывая его без необходимости, изучая новые вещи.

2 голосов
/ 26 мая 2009

(Это написано исключительно с точки зрения программиста. Для более ориентированного на клиента ответа я бы рекомендовал ответ Майкла Болтона.)

Если приложение, которое вы пишете, имеет <10 строк кода, то да, добавление тестов значительно увеличивает сложность. Вы можете посмотреть на него и проверить его вручную, и, вероятно, все будет в порядке. На 100 строк, не так много, 1000 строк, не так много, 10000 строк, 100 000 строк ... и т. Д. </p>

Вторая ось - это изменение. Будет ли эта бухта основываться / когда-либо / изменяться? На сколько? Чем больше код изменится, тем более ценными будут тесты.

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

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

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

1 голос
/ 24 мая 2009

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

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

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

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

1 голос
/ 24 мая 2009

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

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

Одна ошибка, которую я видел, которая приводит к такого рода сложности, - это желание заставить тестирование следовать дизайну, а не наоборот. Может случиться так, что желание придерживаться определенного сложного для тестирования проекта приводит к внедрению всех видов обходных путей для открытия API, а не к разработке более простого и легкого для тестирования API.

...