Можно ли успешно добавить модульное тестирование в существующий производственный проект? Если да, то как и стоит ли это того?

141

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

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

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

Так что я думаю, что я тоже спрашиваю:

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

(Очевидно, что ответ на третий пункт полностью зависит от того, говорите ли вы с руководством или с разработчиком)


Причина награды

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

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

djdd87
источник
11
Обязательная ссылка на книгу Майкла
Фезерса
1
@Mark - Спасибо, это по ссылке, которую я предоставил. Я надеялся получить достойный ответ, не покупая еще одну книгу ... хотя у вас никогда не может быть слишком много книг (если вы не хотите, чтобы работа была сделана).
djdd87
1
Вам действительно нужно прочитать эту книгу :) Это одна из моих любимых книг, которая действительно помогает понять противоречие между рефакторингом и автоматическим тестированием.
мануэль алдана

Ответы:

178

Я ввел модульные тесты для кодовых баз, в которых его раньше не было. В последнем большом проекте, в котором я участвовал, когда я пришел в команду, продукт уже находился в производстве с нулевыми юнит-тестами. Когда я ушел - 2 года спустя - у нас было около 4500 или около того тестов, дающих около 33% покрытия кода в кодовой базе с 230000 + производственным LOC (финансовое приложение Win-Forms в реальном времени). Это может показаться незначительным, но результатом стало значительное улучшение качества кода и количества дефектов, а также повышение морального духа и прибыльности.

Это можно сделать, если у вас есть как точное понимание, так и приверженность заинтересованных сторон.

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

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

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

Теперь для ваших комментариев и вопросов:

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

Краткий ответ: да, вы пропустите тесты, и да, они могут изначально не выглядеть так, как если бы они были в ситуации «зеленого поля».

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

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

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

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

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

Существуют ли какие-либо процессы / шаги, которых следует придерживаться, чтобы гарантировать, что существующие решения правильно протестированы, а не просто внедрены?

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

Обучайте своих людей.

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

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

Время сборки всегда должно быть БЫСТРЫМ. Если ваше время сборки медленное, ваши навыки модульного тестирования отстают. Найдите медленные тесты и улучшите их (разделите производственный код и тестируйте изолированно). Хорошо написано, вы легко сможете провести несколько тысяч модульных тестов и при этом завершить сборку менее чем за 10 минут (~ 1-несколько мс / тест - хороший, но очень грубый ориентир, некоторые исключения могут применяться, например, код с использованием отражения и т. Д. ).

Осмотрите и адаптируйте.

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

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

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

Два приблизительных вторичных показателя - это общее покрытие кода и скорость сборки.

Стоит ли прилагать усилия к существующему решению, которое находится в производстве?

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

Не лучше ли игнорировать тестирование этого проекта и добавить его в возможной будущей переписывании?

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

Мой личный ответ был бы «да, конечно» в большинстве случаев, потому что я знаю это намного лучше, но я понимаю, что могут быть исключения.

Что будет полезнее; потратить несколько недель на добавление тестов или несколько недель на добавление функций?

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

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

Кроме того, добавление модульных тестов к существующей кодовой базе любого значительного размера проекта - это КРУПНАЯ задача, требующая приверженности и настойчивости. Вы не можете изменить что-то фундаментальное, ожидайте многого по пути и попросите своего спонсора не ожидать какой-либо рентабельности инвестиций, останавливая поток бизнес-ценности. Это не сработает, и, честно говоря, не должно.

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

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

надеюсь, это поможет

Махол25
источник
Отражает то же мышление, что и я. В настоящее время я демонстрирую именно этот процесс / метод для внедрения тестовых примеров в большом приложении JAVA. :)
Даршан Джоши
3
Это лучший сформулированный ответ на любую тему, которую я когда-либо читал на Stackoverflow. Отлично сработано! Если вы еще не сделали этого, я бы посоветовал вам подумать о написании книги по вашему ответу на последний вопрос.
Yermo Lamers
Спасибо, Ермо. Я не уверен, что у меня будет время написать книгу. Но, возможно, я мог бы написать пару статей в блоге. Только начинаю свой новый блог, так что это может занять некоторое время.
Mahol25
2
Этот совет по модульному тестированию - общий жизненный совет. Шутки в сторону.
Wjdavis5
24
  • Стоит ли прилагать усилия к существующему решению, которое находится в производстве?

Да!

  • Не лучше ли игнорировать тестирование этого проекта и добавить его в возможной будущей переписывании?

Нет!

  • Что будет полезнее; потратить несколько недель на добавление тестов или несколько недель на добавление функций?

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

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

Затем проведите тест для каждой текущей ошибки в вашей базе данных ошибок, который вызывает именно ошибку и который пройдет, когда ошибка будет исправлена. Тогда исправьте эти ошибки! :-)

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

Donal Fellows
источник
Спасибо за подробный ответ. Подтвердил то, что я чувствовал. Посмотрю, какие другие ответы будут опубликованы, прежде чем отдавать свои голоса, и приму.
djdd87
15

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

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

Флетчер
источник
Если код с самого начала достаточно хорошо разбит на части, его довольно легко протестировать. Проблема возникает, когда у вас есть код, который беспорядочно скомпонован вместе и где единственные выставленные точки тестирования предназначены для тестов полной интеграции. (У меня есть подобный код, где тестируемость почти равна нулю из-за того, что он полагается на другие компоненты, которые нелегко имитировать, и где для развертывания требуется перезагрузка сервера. Тестируемый… но с трудом справляющийся.)
Donal Fellows
13

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

Хотя добавление тестов к существующему коду полезно, оно требует затрат. Это происходит в стоимости не строить новые функции. Как эти две вещи уравновешиваются, полностью зависит от проекта, и есть ряд переменных.

  • Сколько времени у вас уйдет, чтобы протестировать весь этот код? Дней? Недели? Месяцы? Лет?
  • Для кого вы пишете этот код? Платящие клиенты? Профессор? Проект с открытым исходным кодом?
  • Какое у тебя расписание? У вас есть жесткие сроки, которые вы должны соблюдать? У вас вообще есть дедлайны?

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

  • Любой новый код, который вы пишете, должен полностью проходить модульное тестирование.
  • Любой старый код, с которым вы случайно столкнетесь (исправление ошибок, расширение и т. Д.), Следует подвергнуть модульному тестированию.

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

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

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

Это похоже на вопрос: «Каковы плюсы и минусы использования системы контроля версий?» или «Каковы плюсы и минусы собеседования с людьми перед их приемом на работу?» или "Каковы плюсы и минусы дыхания?"

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

Haydenmuhl
источник
9

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

Одним из первых вещей, которые мы сделали (после настройки сервера непрерывной сборки), было добавление регрессионных тестов. Это были сквозные тесты.

  • Каждый набор тестов начинается с инициализации базы данных в известное состояние. На самом деле у нас есть десятки наборов данных регрессии, которые мы храним в Subversion (в отдельном репозитории от нашего кода из-за огромного размера). FixtureSetUp каждого теста копирует один из этих наборов данных регрессии во временную базу данных, а затем запускается оттуда.
  • Затем установка тестового устройства запускает некоторый процесс, результаты которого нас интересуют (этот шаг не является обязательным - некоторые регрессионные тесты существуют только для проверки отчетов).
  • Затем каждый тест запускает отчет, выводит отчет в файл .csv и сравнивает содержимое этого .csv с сохраненным моментальным снимком. Эти CSV-файлы снимков хранятся в Subversion рядом с каждым набором данных регрессии. Если вывод отчета не соответствует сохраненному снимку, тест не пройден.

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

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

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

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

Джо Уайт
источник
8

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

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

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

Хью Брэкетт
источник
6

Добавлю свой голос и скажу да, всегда полезно!

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

  • Черный ящик = тесты, которые написаны без специальных знаний о реализации, обычно выискивая крайние случаи, чтобы убедиться, что все происходит так, как ожидал наивный пользователь.
  • Белый ящик = тесты, написанные с учетом реализации, которые часто пытаются проверить хорошо известные точки отказа.
  • Модульные тесты = тесты отдельных модулей (функций, разделяемых модулей и т. Д.). Например: убедитесь, что ваш класс массива работает должным образом, и что ваша функция сравнения строк возвращает ожидаемые результаты для широкого диапазона входных данных.
  • Функциональные тесты = тесты всей системы одновременно. Эти тесты будут одновременно проверять большую часть системы. Например: init, открыть соединение, сделать что-то из реального мира, закрыть, завершить. Мне нравится проводить различие между этими и модульными тестами, потому что они служат другой цели.

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

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

Как и другие, я также напомню вам две самые важные вещи о TDD:

  1. Создание тестов - это непрерывная работа. Это никогда не прекращается. Вам следует пытаться добавлять новые тесты каждый раз, когда вы пишете новый код или изменяете существующий код.
  2. Ваш набор тестов никогда не бывает безошибочным! Не позволяйте тому факту, что у вас есть тесты, убаюкивает вас ложным чувством безопасности. Тот факт, что он проходит набор тестов, не означает, что он работает правильно, или что вы не внесли тонкого снижения производительности и т. Д.
Дрю Талер
источник
4

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

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

Мой ответ на ваш третий вопрос такой же, как и на первый: это зависит от контекста вашего проекта.

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

Себ Роуз
источник
4

Вы не упоминаете язык реализации, но если в Java, вы можете попробовать такой подход:

  1. При построении отдельного дерева исходных текстов регрессионные или «дымовые» тесты с использованием инструмента для их генерации могут приблизить покрытие к 80%. Эти тесты выполняют все логические пути кода и с этого момента проверяют, что код по-прежнему выполняет именно то, что делает в настоящее время (даже если присутствует ошибка). Это дает вам страховку от непреднамеренного изменения поведения при выполнении необходимого рефакторинга, чтобы код можно было легко тестировать вручную.

    • Предложение по продукту. Раньше я использовал бесплатный веб-продукт Junit Factory, но, к сожалению, сейчас он закрыт. Однако продукт продолжает существовать в коммерчески лицензированном генераторе AgitarOne JUnit Generator по адресу http://www.agitar.com/solutions/products/automated_junit_generation.html.
  2. Для каждой исправляемой ошибки или добавляемой с этого момента функции используйте подход TDD, чтобы новый код был спроектирован так, чтобы его можно было тестировать, и поместите эти тесты в обычное дерево исходного кода.

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

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

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

Крис Б.
источник
3

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

Определите области проектов / модулей, в которых были повторно обнаружены ошибки. Начните с этих проектов, чтобы писать тесты. Совершенно имеет смысл писать тесты на новые функции и исправления ошибок.

Стоит ли прилагать усилия к существующему решению, которое находится в производстве?

Да. Вы увидите, как устраняются ошибки, а обслуживание становится проще.

Не лучше ли игнорировать тестирование этого проекта и добавить его в возможной будущей переписывании?

Я бы рекомендовал начать с этого момента.

Что будет полезнее; потратить несколько недель на добавление тестов или несколько недель на добавление функций?

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

ПК
источник
3

Я очень люблю Refactor the Low-Hang Fruit как ответ на вопрос, с чего начать рефакторинг. Это способ улучшить дизайн, не откусывая больше, чем вы можете проглотить.

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

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

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

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

graham.reeds
источник
2

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

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

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

ты делаешь
источник
2

Обновить

Через 6 лет после первоначального ответа у меня несколько иное мнение.

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

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

Ранее ответ

Я собираюсь поднять здесь несколько бровей :)

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

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

Зачем?

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

  2. Модульные тесты стоят времени! Там, откуда я родом, это драгоценный товар - и бизнес обычно предпочитает лучшую функциональность, чем полный набор тестов.

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

  4. Модульные тесты не повлияют на воспринимаемое качество вашего продукта - качество, которое видит пользователь. Конечно, ваши методы могут работать точно так же, как в первый день, интерфейс между уровнем представления и бизнес-уровнем может быть безупречным, но угадайте, что? Пользователю все равно! Найдите настоящих тестировщиков для тестирования вашего приложения. И чаще всего эти методы и интерфейсы рано или поздно должны измениться.

Что будет полезнее; потратить несколько недель на добавление тестов или несколько недель на добавление функций? - Есть чертовски много вещей, которые вы можете сделать лучше, чем написание тестов - Напишите новые функции, улучшите производительность, улучшите удобство использования, напишите более качественные справочные руководства, исправьте ожидающие ошибки и т. Д.

Не поймите меня неправильно - если вы абсолютно уверены, что в ближайшие 100 лет ничего не изменится, продолжайте, выбейте себя и напишите эти тесты. Автоматические тесты также являются отличной идеей для API, когда вы абсолютно не хотите нарушать сторонний код. В любом другом месте это просто то, что заставляет меня отправлять позже!

Рупеш Шеной
источник
И я призываю вас прочитать это - joelonsoftware.com/items/2009/01/31.html
Рупеш Шеной
Вы бы предпочли «исправить незавершенные ошибки» или предотвратить их появление? Правильное модульное тестирование действительно экономит время, сводя к минимуму время, затрачиваемое на исправление ошибок.
Игорь Кагарличенко
Это миф. Если вы говорите мне, что автоматические модульные тесты заменяют ручное тестирование, то вы серьезно ошибаетесь. А что записывают ручные тестировщики, как не ошибки?
Рупеш Шеной,
И да, не поймите меня неправильно - я не говорю, что модульные тесты - это абсолютная трата - дело в том, чтобы учесть время, необходимое для их написания, и причины, по которым они могут измениться, когда вы меняете продукт, действительно ли они окупаются. Я попробовал обе стороны, и ответ был отрицательным, они не окупаются достаточно быстро.
Рупеш Шеной,
1

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

  • Как вы упомянули, когда вы обнаруживаете ошибку, самое время написать тест (чтобы воспроизвести его), а затем исправить ошибку. Если вы видите, что тест воспроизводит ошибку, вы можете быть уверены, что это хороший тест. Учитывая, что такая большая часть ошибок является регрессией (50%?), Почти всегда стоит писать регрессионные тесты.
  • Когда вы погружаетесь в область кода, чтобы изменить ее, самое время написать для нее тесты. В зависимости от характера кода подходят разные тесты. Здесь можно найти один хороший набор советов .

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

Удачи!

ндп
источник
1

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

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

Что касается новых функций, я бы придерживался подхода «снаружи внутрь»: начните с функций, задокументированных в RSpec и проверенных путем автоматизации пользовательского интерфейса (что, конечно, сначала не удастся), а затем добавляйте более мелкозернистые модульные тесты по мере продвижения реализации.

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

Мммм
источник
1

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

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

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

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

Оттуда я получу ваш сервер сборки или любую другую автоматизацию, которая у вас есть, настроенную с помощью инструмента покрытия кода. Они создают неприятные гистограммы с большими красными зонами там, где у вас плохое покрытие. 100% покрытие - не ваша цель, и 100% покрытие не обязательно означает, что ваш код пуленепробиваемый, но красная полоса определенно мотивирует меня, когда у меня есть свободное время. :)

Дэйв
источник
1

Хороших ответов так много, что я не буду повторять их содержание. Я проверил ваш профиль и похоже, что вы разработчик C # .NET. По этой причине я добавляю ссылку на проект Microsoft PEX и Moles, который может помочь вам с автогенерированием модульных тестов для устаревшего кода. Я знаю, что автогенерация - не лучший способ, но, по крайней мере, это способ начать. Посмотрите эту очень интересную статью из журнала MSDN об использовании PEX для устаревшего кода .

Ладислав Мрнка
источник
1

Я предлагаю читать блестящую статью на более TopTal инженером, который объясняет , где начать добавлять тесты: он содержит много математики, но основная идея заключается в том :

1) Измерьте афферентное сцепление (CA) вашего кода (насколько класс используется другими классами, что означает, что его нарушение может вызвать обширный ущерб)

2) Измерьте цикломатическую сложность (CC) вашего кода (более высокая сложность = более высокая степень нарушения взлома)

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

Зачем? Потому что класс с высоким CA, но с очень низким CC очень важен, но вряд ли сломается. С другой стороны, низкий CA, но высокий CC, скорее всего, сломаются, но принесут меньше повреждений. Итак, вы хотите уравновесить.

Андрейс
источник
0

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

трендл
источник
-2

Да. Нет. Добавление тестов.

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

Индра
источник