Как удалить функцию или функцию при использовании TDD


20

В текстах о TDD я часто читал об «устранении дублирования» или «улучшении читаемости» на этапе рефакторинга. Но что заставляет меня удалить неиспользуемую функцию?

Например, скажем, есть класс Cс методами a()и b(). Теперь я думаю, что было бы неплохо иметь метод, f()который используется C. Фактически f()заменяет все вызовы, b()за исключением модульных тестов, которые определены / описаны b(). Это больше не нужно - за исключением тестов.

Это сохранить, чтобы просто удалить b()и все тесты, которые его использовали? Это часть «улучшения читабельности»?


3
Просто добавьте еще один тест, чтобы проверить, что функция не существует, а затем исправьте неисправные тестовые случаи;)
Филипп Хаглунд

@FilipHaglund В зависимости от языка это может быть возможно. Но с точки зрения документации кода это будет выглядеть странно.
TobiMcNamobi

1
Просто для тех, кто не знает лучше: комментарий @ FilipHaglund, очевидно, шутка. Не делай этого.
Джиот

Ответы:


16

Да, конечно. Самый простой код для чтения - это то, чего нет.

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


@ jpmc26 Agile, чувак, Agile! :-)
TobiMcNamobi

1
«Самый простой код для чтения - это то, чего нет». - Я
вешаю

27

Удаление открытого метода не является «рефакторингом» - рефакторинг меняет реализацию, продолжая проходить существующие тесты.

Однако удаление ненужного метода - совершенно разумное изменение дизайна.

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

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

Есть два подхода к удалению метода; оба работают в разных обстоятельствах:

  1. Удалить метод. Следуйте ошибкам компиляции, чтобы удалить любой зависимый код и тесты. Если вы уверены, что соответствующие тесты являются одноразовыми, внесите изменения. Если нет, откат.

  2. Удалите тесты, которые вы считаете устаревшими. Запустите весь свой набор тестов с покрытием кода. Удалите методы, которые не были выполнены тестовым набором.

(Это предполагает, что ваш набор тестов имеет хороший охват с самого начала).


10

Фактически f () заменяет все вызовы b () за исключением модульных тестов, которые определили / описали b ()

ИМХО типичный цикл TDD будет выглядеть так:

  • написать неудачные тесты для f () (возможно, на основе тестов для b ()): тесты идут красным

  • реализовать f () -> тесты становятся зелеными

  • рефакторинг : -> удалить b () и все тесты для b ()

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


4

Да, это так.

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


9
Вы пропустили "как" расстаться.
JeffO

2

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

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

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

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

Цикл красный / зеленый / рефактор явно не упоминает удаление тестов. Кроме того, удаление b()нарушает открытый / закрытый принцип , так как очевидно , ваш компонент является открытым для модификации. Так что если вы хотите думать об этом шаге как о чем-то, что выходит за рамки простого TDD, продолжайте. Например, у вас может быть какой-то процесс для объявления теста «плохим», который можно применить в этом случае, чтобы удалить тест на том основании, что он проверяет что-то, чего не должно быть (ненужная функция b()).

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

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


2

Одна вещь, которую всегда нужно помнить, это то, что мы сейчас используем КОДОВЫЕ Хранилища с VERSION CONTROL. Этот удаленный код на самом деле не ушел ... он все еще где-то в предыдущей итерации. Так снеси это! Будьте осторожны с ключом удаления, потому что вы всегда можете вернуться и получить тот драгоценный элегантный метод, который, как вы думаете, может быть полезен когда-нибудь ... если когда-нибудь это когда-нибудь произойдет. Это здесь.

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


Удаление кода не является проблемой в целом. Я люблю удалять, удалять строки, функции или ... ну, целые модули! Если возможно. И я делаю это все с или без TDD. Но: есть ли точка в рабочем процессе TDD, где (не дублированный) код удаляется? Если нет, он все равно будет удален. А есть ли? Это был мой вопрос.
TobiMcNamobi
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.