Предпосылка приоритета трансформации - это руководство для более аналитического подхода к разработке через тестирование. Это набор правил, которые должны помочь в написании тестов TDD.
Если вы раньше не слышали о TPP, но знакомы с TDD, то основная посылка (не каламбур) такая же. Вы всегда должны вносить минимально возможные изменения. Однако порядок, в котором вы применяете изменения, имеет большое значение для качества и количества производимых тестов. Вот как это объясняет дядя Боб:
«У рефакторинга есть аналоги, называемые преобразованиями. Рефакторинг - это простые операции, которые изменяют структуру кода без изменения его поведения. Преобразования - это простые операции, изменяющие поведение кода. Преобразования могут использоваться как единственное средство для прохождения текущего неуспешного теста в цикле красный / зеленый / рефакторинг. Преобразования имеют приоритет или предпочтительный порядок, который, если его поддерживать в соответствии с порядком проведения тестов, предотвратит тупиковые ситуации или длительные простои в цикле красный / зеленый / рефакторинг ».
- « Дядя Боб Мартин», « Предпосылка трансформации , Блог дяди Боба
Правила
После попытки написать примеры преобразований для всех правил я обнаружил, что некоторые из них очень трудно четко продемонстрировать с помощью этой программной идеи (описанной ниже). Возможно, эту страницу нужно изменить (трансформировать, если хотите), чтобы использовать примеры в разных обстоятельствах. Пожалуйста, дайте мне знать, если хотите помочь.
Фрагменты кода написаны на Python. Первоначальная общая идея заключалась в том, чтобы представить, что вы работаете над программой, которая переводит двузначный код страны в название страны, чтобы не было необходимости в большом контексте кода для понимания каждого из преобразований.
Важно понимать, что примеры приведены не по порядку, а сами тесты не будут работать вместе. Каждый шаг отображается так:
# The existing code
↓
# The red (failing) test. # The new code using that transformation. # It will also make the test go green (pass).
1. {} → ноль
Преобразуйте без кода во что-то, что возвращает нулевое значение.
без кода
↓
def test_bad_country_code_returns_none(): assert country_from_code(None) is None def country_from_code(code): return None
2. ноль → константа
def country_from_code(code): return None
↓
def test_country_is_a_string(): assert type(country_from_code('AU')) is str def country_from_code(code): return “"
3. константа → константа +
Преобразование от простой константы к более сложной константе.
def country_from_code(code): return ""
↓
def test_au_is_australia(): assert country_from_code('AU') == ‘Australia' def country_from_code(code): return “Australia"
4. константа → скаляр
Замена константы переменной или аргументом.
def country_from_code(code): return “"
↓
def test_au_is_australia(): assert country_from_code('AU') == ‘Australia' def country_from_code(code): country = "Australia" return country
5. выписка → выписки
Добавление еще безусловных утверждений.
Это плохой пример, потому что нет необходимости в дальнейшей обработке названия страны в каком-либо конкретном случае, поэтому мы не можем действительно оправдать новые строки, которые не имеют никакого практического использования.
def country_from_code(code): country = "australia" return country
↓
def test_au_is_australia(): assert country_from_code('AU') == ‘Australia' def country_from_code(code): country = "australia" country = country[0].upper() + country[1:] return country
6. безусловный → если
Разделение пути выполнения. Также допустимо использовать вместо этого троичную систему.
def country_from_code(code): return “Australia"
↓
def test_es_is_spain(): assert country_from_code('ES') == ‘Spain' def country_from_code(code): if (code == 'ES'): return "Spain" return “Australia"
7. скаляр → массив
def country_from_code(code): if (code == 'ES'): return "Spain" return “Australia"
↓
def test_dk_is_denmark(): assert country_from_code('DK') == ‘Denmark' def country_from_code(code): countries = ('AU', 'Australia', 'DK', 'Denmark', 'ES', 'Spain') return countries[countries.index(code) + 1]
8. массив → контейнер
Обычно это означает преобразование простого массива в более сложную коллекцию, такую как словарь или массив объектов.
def country_from_code(code): countries = ('AU', 'Australia', 'DK', 'Denmark', 'ES', 'Spain') return countries[countries.index(code) + 1]
↓
def test_mx_is_mexico(): assert country_from_code('MX') == ‘Mexico' def country_from_code(code): countries = { 'AU': 'Australia', 'DK': 'Denmark', 'ES': 'Spain', 'MX': 'Mexico' } return countries[code]
9. оператор → хвостовая рекурсия
В этом случае я преобразую оператор, который вычисляет индекс, в рекурсию, которая пробует элементы по одному, пока не будет найден правильный. На самом деле это было бы очень плохим решением, но я придерживаюсь темы этих примеров.
Хвостовая рекурсия означает, что последняя инструкция (обычно возвращаемое значение) является рекурсивным вызовом.
def country_from_code(code): countries = ('AU', 'Australia', 'DK', 'Denmark') return countries[countries.index(code) + 1]
↓
def test_nz_is_new_zealand(): assert country_from_code('NZ') == 'New Zealand’ def country_from_code(code, index=0): countries = ('AU', 'Australia', 'DK', 'Denmark', 'NZ', 'New Zealand') if countries[index] == code: return countries[index + 1] return country_from_code(code, index + 2)
10. если → пока
Преобразуйте бинарный оператор ветвления (if, если, если, ternary и т. Д.) В цикл (while, do, for и т. Д.).
def country_from_code(code): if (code == 'ES'): return "Spain" return “Australia"
↓
def test_th_is_thailand(): assert country_from_code('TH') == ‘Thailand' def country_from_code(code): countries = (('AU', 'Australia'), ('ES', 'Spain'), ('TH', 'Thailand')) for country in countries: if country[0] == code: return country[1] return None
11. оператор → без хвостовой рекурсии
Не хвостовая рекурсия означает, что последняя инструкция (обычно возвращаемое значение) не является рекурсивным вызовом.
def country_from_code(code): countries = ('AU', 'Australia', 'DK', 'Denmark') return countries[countries.index(code) + 1]
↓
def test_de_is_germany(): assert country_from_code('DE') == ‘Germany' def country_from_code(code, index=0): countries = ('AU', 'Australia', 'DK', 'Denmark', 'DE', 'Germany') if countries[index] != code: country = country_from_code(code, index + 2) else: country = countries[index + 1] return country
12. выражение → функция
Замена выражения функцией или алгоритмом.
def country_from_code(code): countries = { 'AU': 'Australia', 'DK': 'Denmark', 'ES': 'Spain' } return countries[code]
↓
def test_it_is_italy(): assert country_from_code('IT') == ‘Italy' def get_country_codes(): return { 'AU': 'Australia', 'DK': 'Denmark', 'ES': 'Spain', 'IT': 'Italy' } def country_from_code(code): countries = get_country_codes() return countries[code]
13. переменная → присвоение
Замена значения переменной.
def country_from_code(code): if code == 'ES': return 'Spain' return ‘Australia'
↓
def test_dk_is_denmark(): assert country_from_code('DK') == ‘Denmark' def country_from_code(code): country = 'Australia' if code == 'ES': country = 'Spain' if code == 'DK': country = 'Denmark' return country
14. чехол
Добавление case (или еще) к существующему переключателю или if.
def country_from_code(code): if (code == 'ES'): return "Spain" return “Australia"
↓
def test_dk_is_denmark(): assert country_from_code('DK') == ‘Denmark' def country_from_code(code): if (code == 'ES'): return "Spain" if (code == 'DK'): return "Denmark" return “Australia"
Дополнительная информация
Я рекомендую вам прочитать оригинальную статью от дяди Боба.
Я упоминал выше, что эта статья действительно требует гораздо большего внимания, чтобы предложить лучшие примеры для некоторых преобразований, пожалуйста, свяжитесь со мной, если у вас есть какие-либо идеи.
Первоначально опубликовано на http://elliot.land 7 мая 2016 г.