Что поможет при рефакторинге большого метода, чтобы я ничего не сломал?

10

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

Обратите внимание, что рефакторинг включает в себя перевод унаследованного кода C # в более функциональный стиль (в унаследованном коде не используются какие-либо функции .NET Framework 3 и более поздних версий, включая LINQ), добавление обобщений, где код может извлечь из них пользу, и т. Д.

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

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

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

  • Точно знать, какие юнит-тесты я должен создать,

  • И / или знаете, повлияло ли изменение, которое я сделал, на исходный код так, как он выполняется по-другому?

Арсений Мурзенко
источник
Что вы думаете о том, что написание юнит-тестов увеличит время для этого проекта? Многие сторонники не согласятся, но это также зависит от вашей способности писать их.
JeffO
Я не говорю, что это увеличит общее время проекта. Что я хотел сказать, так это то, что это увеличит кратковременное время (т. Е. Непосредственное время, которое я трачу прямо сейчас при рефакторинге кода).
Арсений Мурзенко
1
В formal methods in software developmentлюбом случае, вы не захотите использовать его, потому что он используется для доказательства правильности программы, использующей логику предикатов, и не может применяться для рефакторинга большой кодовой базы. Формальные методы, обычно используемые для проверки кода, работают правильно в таких областях, как медицинские приложения. Вы правы, делать это дорого, поэтому его не часто используют.
Mushy
Хороший инструмент, такой как опции рефакторинга в ReSharper , значительно облегчает эту задачу. В таких ситуациях это стоит денег.
billy.bob
1
Не полный ответ, а глупый метод, который я нахожу удивительно эффективным, когда все другие методы рефакторинга дают сбой: создайте новый класс, разбейте функцию на отдельные функции с точно таким же кодом, который уже существует, просто разбитые каждые 50 или около того строк, продвигайте любые локальные объекты, которые разделяются между функциями для членов, тогда отдельные функции лучше вписываются в мою голову и дают мне возможность видеть в членах, какие части пронизывают всю логику. Это не конечная цель, просто безопасный способ получить устаревший беспорядок, чтобы быть готовым к безопасному рефакторингу.
Джимми Хоффа

Ответы:

12

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

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

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

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

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

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


источник
2
+1 за использование только «археологии». Это тот же термин, который я использую, чтобы описать это упражнение, и я думаю, что это отличный способ выразить его (также я подумал, что ответ был хорошим - я не такой уж мелкий)
Эрик Дитрих
10

Что поможет при рефакторинге большого метода, чтобы я ничего не сломал?

Краткий ответ: маленькие шаги.

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

Рассмотрим эти шаги:

  1. Переместите реализацию в другую (приватную) функцию и делегируйте вызов.

    // old:
    private int ugly500loc(int parameters) {
        // 500 LOC here
    }
    
    // new:    
    private int ugly500loc_old(int parameters) {
        // 500 LOC here
    }
    
    private void ugly500loc(int parameters) {
        return ugly500loc_old(parameters);
    }
    
  2. Добавьте код регистрации (убедитесь, что регистрация не завершилась) в исходной функции для всех входов и выходов.

    private void ugly500loc(int parameters) {
        static int call_count = 0;
        int current = ++call_count;
        save_to_file(current, parameters);
        int result = ugly500loc_old(parameters);
        save_to_file(current, result); // result, any exceptions, etc.
        return result;
    }
    

    Запустите ваше приложение и сделайте с ним все, что можете (действительное использование, недопустимое использование, обычное использование, нетипичное использование и т. Д.).

  3. Теперь у вас есть max(call_count)наборы входов и выходов для написания ваших тестов; Вы можете написать один тест, который перебирает все ваши параметры / наборы результатов и выполняет их в цикле. Вы также можете написать дополнительный тест, который запускает определенную комбинацию (для быстрой проверки прохождения определенного набора ввода / вывода).

  4. Переместить // 500 LOC hereобратно в ugly500locфункцию (и удалить при входе функции).

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

  6. Жить долго и счастливо.

utnapistim
источник
3

Обычно модульные тесты - это путь.

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

Что может помочь мне узнать, какие модульные тесты актуальны для данного фрагмента кода?

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

Тогда вы можете разорвать все на части без проблем.

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

Ура и удачи!

Alex

AlexCode
источник
Инструменты покрытия кода здесь важны. Подтвердить, что вы прошли все сложные и сложные методы проверки, сложно. Инструмент, который показывает, что KitchenSinkMethodTest01 () ... KitchenSinkMethodTest17 () вместе охватывает строки 1-45, 48-220, 245-399 и 488-500, но не затрагивает код между ними; Выяснить, какие дополнительные тесты вам нужно написать, будет намного проще.
Дэн возится с огнем