Практический рефакторинг с использованием модульных тестов

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

Итак, мой первый вопрос таков: как выполнить модульное тестирование метода в унаследованном коде? Как я могу провести модульный тест вокруг метода из 500 строк (если мне повезет), который не выполняет только одну задачу? Мне кажется, что мне придется реорганизовать мой унаследованный код только для того, чтобы сделать его пригодным для модульного тестирования.

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

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

Наконец, поскольку несколько человек спросили... устаревший код был первоначально написан на VB6, а затем перенесен на VB.NET с минимальными изменениями архитектуры.


person Community    schedule 06.02.2009    source источник
comment
Отличный вопрос. Вы также можете попробовать Katas, который поможет вам выработать привычку писать хороший код и узнать, как писать код для модульного тестирования: github.com/garora/TDD-Katas   -  person Gaurav Arora    schedule 07.08.2017


Ответы (7)


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

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

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

person MrTelly    schedule 06.02.2009
comment
Я часто ловлю себя на том, что выступаю за компромисс с прагматической стороны такого пуристского обсуждения. Может быть, я просто старею, и у меня больше нет сил возглавить пуристическую атаку. ;) - person JMD; 07.02.2009
comment
@ JMD. Более того, единственный модульный тест, имеющий значение для рефакторинга, - это сквозной функциональный модульный тест с минимальной зависимостью от внутренней структуры. При этом об этом никто не говорит. Я предполагаю, что происходит очень мало рефакторинга: существующий код настолько плох, что в большинстве случаев рефакторинг выполняется путем замены функциональных блоков. - person zzz777; 25.10.2014

Инструкции по рефакторингу устаревшего кода можно найти в книге Эффективная работа с устаревшим кодом. Также доступна короткая версия в формате PDF здесь.

person Esko Luontola    schedule 06.02.2009

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

person Fabian Steeg    schedule 06.02.2009

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

person Robin    schedule 06.02.2009

По моему опыту, это реальность при работе с устаревшим кодом. Книга (Working with Legacy..), упомянутая Эско, — отличная работа, описывающая различные подходы, которые могут привести вас к этому.

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

Кстати, недавно Джоэл С. и Мартин Ф. говорили о TDD/модульных тестах. Я считаю, что важно определить единицу измерения и сосредоточиться на ней! URL-адреса: Открытое письмо, расшифровка стенограммы Джоэла и подкаст

person Ketan    schedule 07.02.2009

Это действительно одна из ключевых проблем при попытке переделать устаревший код. Можете ли вы разбить проблемную область на что-то более детальное? Делает ли этот метод из 500+ строк что-либо, кроме системных вызовов JAR/DLL/сборок JDK/Win32/.NET Framework? т.е. Есть ли более детализированные вызовы функций в этом бегемоте из 500+ строк, которые вы могли бы протестировать?

person JMD    schedule 06.02.2009
comment
Действительно, есть более детализированные функции, но как я могу их протестировать без предварительного рефакторинга унаследованного кода для извлечения более гранулярных методов? - person ; 07.02.2009
comment
Это вопрос на 64 000 долларов. И иногда ответом является компромисс. Вы хотите добиться идеального рефакторинга, но иногда вам приходится писать модульные тесты, которые вы можете /пока/ вы улучшаете унаследованному коду. По крайней мере, это был мой опыт. - person JMD; 07.02.2009

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

Я нашел это весьма полезным.

person ratkok    schedule 28.05.2010