Рефакторинг старого кода без тестов

Рефакторинг старого кода без тестов

Существует система, написанная в ОО-стиле, но о TDD автор кода в то время не знал. Вопрос: каким образом лучше внедрить тесты в уже написанный код?

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

Может существует какая-то литература по этой теме?
 

svetasmirnova

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

syfisher

TDD infected!!
Это все зависит от того, что вы хотите получить от тестов. Просто так писать тесты на существующий код - это пустая трата времени. Должна быть какая-то причина необходимости добавления тестов: например дальнейшее развитие проекта и необходимость рефакторинга. Кстати тот факт, что код был написан без учета test-ability может быть очень серьезным препятствием для вредрения тестов. Обязательно будете готовы к тому, что код будет "сопротивляться" модульному тестированию.

Если у вас есть необходимость добавить функциональность, а тестов нет - значит вы имеете деле с так называемым legacy code. Есть книга M.Feathers "Working Effectively with Legacy Code", но она не проста для понимания и требует очень серьезного опыта тестирования.

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

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

Вообще эта тема очень сложная и имеет мало общего с чистым TDD.

-~{}~ 08.11.05 17:17:

P.S. Если интересно, то тему можно продолжить. Я могу попробовать накатать некоторые рекомендации. Но на это может потребоваться время.
 
Написание тестов перед кодом решает проблему, что тестировать. А без автоматического тестирования расширять функциональность стало всё сложнее. Один способ написания тестов пост-мортем мне известен - как только обнаруживается баг, пишется тест конкретно на него. Но это не дает уверенности, что все баги найдены.

Автор старого кода я, поэтому извращаться с самим кодом из-за разного понимания ООП не нужно.
 

syfisher

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

Я так понял, что ты хочешь попробовать начать использовать модульное тестирование (опыта тестирования до этого не было) и у тебя есть уже определенная кодовая база, которую бросать не хотелось бы. Я сейчас заканчиваю статью на тему "Ошибки начинающих TDD-практиков", думаю, что тебе будет интересно ее прочитать. Статья, надеюсь, выйдет в след. номере phpinside. Если есть желание почитать пораньше - стучись в личку, вышлю черновик мылом.
 
Как автоматическое тестирование может стать тормозом? Неполное покрытие кода тестами не дают уверенности в полной работоспособности системы, но любой тест лучше его отсутствия.

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

syfisher

TDD infected!!
Автор оригинала: Алексей Пешков
Как автоматическое тестирование может стать тормозом?
Неправильно написанные тесты (большие, хрупкие, сложные) часто ломаются, требуют частого вмешательства, времени на понимание. Так они становятся тормозом. Важно понимать, почему они становятся такими и как этого избежать.

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

Еще раз повторю, в чем проблема. Существующий код имеет детали (например assert на неожиданные входные параметры или htmlspecialchars() на значения, которые в данный момент подразумеваются чисто буквенно-цифровыми). Подобная функциональность с точки зрения TDD является излишней и по идее должна подлежать редукции, но без тестов модулей вызывающих данный, невозможно быть уверенным в этом. Зафиксировать эти детали в тесте означает зафиксировать потенциально лишний код, но без тестов невозможно убедиться, что это код дейтвительно лишний.
Наверное, все равно придется фиксировать существующую функциональность. Ведь нужно сначала получить "зеленую линию", только затем приступать к рефакторингу.
 

pachanga

Новичок
syfisher
Это все зависит от того, что вы хотите получить от тестов. Просто так писать тесты на существующий код - это пустая трата времени.
Я так понимаю тема называется "Рефакторинг старого кода..." - разве это не причина?

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

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

Здесь надо учитывать, насколько ценен старый код. Если существует целая работающая инфраструктура, полностью оправдывающая свое существование, и отказ от которой(т.е. написание с нуля) сродни самоубийству, то без тестов никуда. Так что вооружайся "Working Effectively with Legacy Code" и вперед.

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

Автор оригинала: Алексей Пешков
Как автоматическое тестирование может стать тормозом? Неполное покрытие кода тестами не дают уверенности в полной работоспособности системы, но любой тест лучше его отсутствия.
Согласен на 200%.
 

tz

Новичок
моё мнение по топику...

внедрять тесты надо...
если ваша система работала на отлично, то внедрение тестов для вас необходимо лишь на добавляемый фунционал.
 

robocomp

Новичок
Насчет "любой тест лучше его отстутсвия"
У нас в нашей компании мечты некоторые разработчики часто говорят что-то типа: "у-у-у-у, тесты. йо-майо! всё не потестировать всё равно. как быть с тем, что всё равно не удастся покрыть тестами все случаи?". Очень адекватным является понимаение того, что когда тестов нет, код существенно меньше покрыт тестами, чем когда они есть, даже если они при этом покрывают не всё.
 
Сверху