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

#c# #unit-testing

#c# #модульное тестирование

Вопрос:

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

Клиентский продукт — это то, что я хочу модульно протестировать, но в нем более 1,2 миллиона строк кода C #, и это очень старый продукт. Он не был разработан с учетом модульного тестирования, и ведущие разработчики этого продукта, как правило, выступают против модульного тестирования в основном из-за соображений соотношения риска и вознаграждения, а также из-за того, что потребуется редизайн, чтобы уменьшить количество насмешек, которые необходимо было бы выполнить. Редизайн этих базовых низкоуровневых клиентских библиотек и объектов также затрагивает их.

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

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

Комментарии:

1. На мой взгляд, риск связан с не модульным тестированием…

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

3. Любопытно, каковы их предполагаемые риски? Находите ошибки? (Серьезно, я полагаю, наиболее вероятно, что они считают код без ошибок, поскольку он выполнялся так долго, так зачем «тратить» время на написание тестов, чтобы уловить очень мало?) Я определенно согласен с Джоном.

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

5. @jamietre Я полностью согласен. У нас такой же подход, где я работаю. Старые, непроверенные продукты не переписываются ради модульных тестов, но по мере их рефакторинга пишутся модульные тесты. Аналогично, весь новый код также содержит это. Однако слегка подразумевалось, что соотношение риска и вознаграждения включает модульное тестирование в целом. Глупо переписывать что-то только для того, чтобы это протестировать (вы можете внести свои собственные ошибки, уничтожив ожидаемое поведение), но новый код, безусловно, должен содержать модульные тесты, а старый код должен постепенно проходить тестирование по мере его рефакторинга. Мы согласны. 🙂

Ответ №1:

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

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

Комментарии:

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

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

3. Менеджеры / сторонники модульного тестирования всегда сопротивляются; их жизненный путь — предотвращать риски. Лучший аргумент, который я когда-либо слышал, это то, что это ускоряет время выхода на рынок; изначально, да, замедление есть. Но позже, когда вам не придется беспокоиться о непреднамеренных побочных эффектах, это определенно ускорит разработку.

Ответ №2:

Я обнаружил, что при изучении этой темы была полезна эффективная работа Майкла Фезерса с устаревшим кодом.

Ответ №3:

Мой опыт:

  1. Реализовать способ системных (сквозных) тестов
  2. При добавлении новой функциональности пишите системные тесты и разрабатывайте новую функциональность с помощью модульных тестов
  3. При изменении существующей функциональности заранее пишите системные тесты
  4. НЕ пытайтесь переписывать существующие модули для тестирования их с помощью модульных тестов

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

Ответ №4:

Я с вашими ведущими разработчиками. Это 1,2 миллиона строк кода, что означает, что при тестировании и редизайне остается ДОСТАТОЧНО места для ошибок. Он не предназначен для UT, поэтому, вероятно, потребуется нетривиальное усилие, чтобы переписать код, чтобы его можно было протестировать. Я уверен, что модульное тестирование заняло бы очень много времени. Кроме того, это старый продукт, который, предположительно, означает, что многие ошибки уже найдены и исправлены. Если это не сломано, не исправляйте это.Не лучше ли вам перейти к более интересным аспектам проекта, чем тестирование и рефакторинг старого кода?

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