Рефакторинг и тестовая разработка - PullRequest
5 голосов
/ 18 марта 2009

Я сейчас читаю две прекрасные книги "Эффективная работа с устаревшим кодом" и "Чистый код".

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

Это привело к двум вопросам:

Вопрос 1: Если я работаю с устаревшим кодом. Согласно книгам, я должен поставить тесты, чтобы убедиться, что я ничего не сломал. Учтите, что у меня есть метод длиной 500 строк. Я предполагаю, что у меня будет набор эквивалентных методов тестирования для тестирования этого метода. Когда я разделяю эту функцию, я создаю новые тесты для каждого нового метода / класса, который приводит?

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

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

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

Ответы [ 6 ]

4 голосов
/ 18 марта 2009
  1. Это дело при работе с устаревшим кодом. Наследие означает систему без тестов, которая тесно связана. При добавлении тестов для этого кода вы фактически добавляете интеграционные тесты. Когда вы реорганизуете и добавляете более конкретные методы тестирования, которые избегают сетевых вызовов и т. Д., Это будут ваши модульные тесты. Вы хотите оставить оба, просто разделите их так, чтобы большинство ваших модульных тестов работали быстро.
  2. Вы делаете это очень маленькими шагами. Вы на самом деле постоянно переключаетесь между тестами и кодом, и вы правы, если вы изменяете подпись (небольшой шаг), связанные с ней тесты должны быть обновлены.

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

2 голосов
/ 18 марта 2009

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

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

1 голос
/ 18 марта 2009

Когда у вас есть длинный устаревший метод, который выполняет X (и, возможно, Y и Z из-за его размера), реальный трюк - не сломать приложение, «исправив» его. Тесты унаследованного приложения имеют предусловия и постусловия, поэтому вы должны действительно знать их, прежде чем разбивать его. Тесты помогают облегчить это. Как только вы разбиваете этот метод на два или более новых метода, очевидно, что вам необходимо знать предварительные / последующие состояния для каждого из них, и поэтому тесты для тех, кто «держит вас честными» и позволяют вам спать лучше ночью.

Я не слишком беспокоюсь о 1/10 секунды утверждения. Скорее, цель, когда я пишу модульные тесты, состоит в том, чтобы охватить все мои основы. Очевидно, что если тестирование занимает много времени, это может быть связано с тем, что то, что тестируется, - это просто слишком много кода, выполняющего слишком много.

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

Каждый раз, когда код изменяется, тесты могут измениться, и, вероятно, потребуется добавить новые, чтобы учесть изменения, внесенные в производственный код. Эти тесты работают с текущим кодом - не имеет значения, нужно ли изменять параметры, все еще существуют условия до / после, которые должны быть выполнены. Очевидно, недостаточно просто разбить код на более мелкие куски. «Аналитик» в вас должен уметь понимать систему, которую вы строите - это первая работа.

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

Удачи!

1 голос
/ 18 марта 2009
    • Да, создавать новые тесты для новых методов.

    • Я бы увидел 1/10 секунды как цель, к которой нужно стремиться. Более медленный тест все же намного лучше, чем без теста.

  1. Старайтесь не менять код и тест одновременно. Всегда делайте маленькие шаги.

0 голосов
/ 18 марта 2009

Вопрос 1: «Когда я разделяю эту функцию, я создаю новые тесты для каждого нового метода / класса, который получается?»

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

Вопрос 2: «Что происходит, когда код подвергается такому факторингу, что структурно он больше не напоминает исходный код?»

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

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

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

0 голосов
/ 18 марта 2009

Вот мое мнение:

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

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

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

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

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